diff --git a/.gitignore b/.gitignore index 462a29633b..85bd1148b9 100644 --- a/.gitignore +++ b/.gitignore @@ -24,8 +24,6 @@ config/database.yml config/app_config.yml config/redis.conf src/ -!lib/build/src/ -lib/build/test/test_bundle.* spec/support/data/failed_remote/* *.swp *.swo @@ -40,19 +38,19 @@ services/importer/spec/factories/database.json cartodb.sublime-workspace cartodb.sublime-project .rvmrc -lib/build/dist -lib/build/node_modules app/assets/images/sprites/*.png npm-debug.log -.idea .ruby-gemset lib/build/dist -lib/build/node_modules -lib/build/.grunt/* lib/build/grunt-aws.json lib/build/app_config.js rubygems -lib/build/_SpecRunner.html vendor/bundle/* .vagrant Vagrantfile +node_modules +.grunt/* +_SpecRunner.html +lib/assets/test/cartodb2_tests.js +vendor/assets/javascripts/cartodb.* +vendor/assets/stylesheets/cartodb.* diff --git a/lib/build/Gruntfile.js b/Gruntfile.js similarity index 81% rename from lib/build/Gruntfile.js rename to Gruntfile.js index 4b34ed7452..eba37a3848 100644 --- a/lib/build/Gruntfile.js +++ b/Gruntfile.js @@ -10,8 +10,8 @@ if (timer) timer.init(grunt); - var ROOT_ASSETS_DIR = '../../public/assets/'; - var ASSETS_DIR = '../../public/assets/<%= pkg.version %>'; + var ROOT_ASSETS_DIR = './public/assets/'; + var ASSETS_DIR = './public/assets/<%= pkg.version %>'; // use grunt --environment production var env = grunt.option('environment') || 'development'; @@ -25,50 +25,49 @@ grunt.initConfig({ pkg: grunt.file.readJSON('package.json'), aws: aws, - env: grunt.file.readJSON("./config/" + env + ".json"), + env: grunt.file.readJSON("./lib/build/config/" + env + ".json"), gitrev: exec('git rev-parse HEAD', { silent:true }).output.replace('\n', ''), assets_dir: ASSETS_DIR, root_assets_dir: ROOT_ASSETS_DIR, // Concat task - concat: require('./tasks/concat').task(), + concat: require('./lib/build/tasks/concat').task(), // JST generation task - jst: require('./tasks/jst').task(), + jst: require('./lib/build/tasks/jst').task(), // Compass files generation - compass: require('./tasks/compass').task(), + compass: require('./lib/build/tasks/compass').task(), // Copy assets (stylesheets, javascripts, images...) - copy: require('./tasks/copy').task(grunt), + copy: require('./lib/build/tasks/copy').task(grunt), // Watch actions - watch: require('./tasks/watch.js').task(), + watch: require('./lib/build/tasks/watch.js').task(), // Clean folders before other tasks - clean: require('./tasks/clean').task(), + clean: require('./lib/build/tasks/clean').task(), // Jasmine tests - jasmine: require('./tasks/jasmine.js').task(), + jasmine: require('./lib/build/tasks/jasmine.js').task(), - s3: require('./tasks/s3.js').task(), + s3: require('./lib/build/tasks/s3.js').task(), - uglify: require('./tasks/uglify.js').task(), + uglify: require('./lib/build/tasks/uglify.js').task(), - browserify: require('./tasks/browserify.js').task(), - exorcise: require('./tasks/exorcise.js').task() + browserify: require('./lib/build/tasks/browserify.js').task() }); // Load Grunt tasks require('load-grunt-tasks')(grunt); - require('./tasks/manifest').register(grunt, ASSETS_DIR); + require('./lib/build/tasks/manifest').register(grunt, ASSETS_DIR); // builds cdb grunt.registerTask('cdb', "builds cartodb.js", function() { var done = this.async(); - require("child_process").exec('cd ../../ && make update_cdb', function (error, stdout, stderr) { + require("child_process").exec('cd && make update_cdb', function (error, stdout, stderr) { if (error) { grunt.log.fail('cartodb.js not updated'); } else { diff --git a/app_config.js b/app_config.js new file mode 100644 index 0000000000..90b7c41d06 --- /dev/null +++ b/app_config.js @@ -0,0 +1,2 @@ +cdb.config.set('assets_url', '/assets/2.15.16'); +console.log('cartodbui v2.15.16 sha1: 7262e09e97ea72f91a682d1af7d69126c35b7055'); \ No newline at end of file diff --git a/lib/build/config.rb b/config.rb similarity index 77% rename from lib/build/config.rb rename to config.rb index b93bd3c63f..14d69f1882 100644 --- a/lib/build/config.rb +++ b/config.rb @@ -1,11 +1,12 @@ +# Compass configuration, used by cartodb UI grunt task. # Require any additional compass plugins here. # Set this to the root of your project when deployed: http_path = "/" css_dir = "dist/css" -sass_dir = "../../app/assets/stylesheets/tmp/" -images_dir = "../../app/assets/images/" -#javascripts_dir = "javascripts" +sass_dir = "app/assets/stylesheets/tmp/" +images_dir = "app/assets/images/" +#javascripts_dir = "lib/assets/javascripts" # You can select your preferred output style here (can be overridden via the command line): output_style = :compact @@ -23,4 +24,4 @@ line_comments = false # and then run: # sass-convert -R --from scss --to sass sass scss && rm -rf sass && mv scss sass -Encoding.default_external = "utf-8" \ No newline at end of file +Encoding.default_external = "utf-8" diff --git a/lib/build/src/dashboard.js b/lib/assets/javascripts/cartodb2/dashboard.js similarity index 100% rename from lib/build/src/dashboard.js rename to lib/assets/javascripts/cartodb2/dashboard.js diff --git a/lib/build/src/dashboard/hello_world.js b/lib/assets/javascripts/cartodb2/dashboard/hello_world.js similarity index 100% rename from lib/build/src/dashboard/hello_world.js rename to lib/assets/javascripts/cartodb2/dashboard/hello_world.js diff --git a/lib/build/test/spec/dashboard/hello_world.spec.js b/lib/assets/test/spec/cartodb2/dashboard/hello_world.spec.js similarity index 100% rename from lib/build/test/spec/dashboard/hello_world.spec.js rename to lib/assets/test/spec/cartodb2/dashboard/hello_world.spec.js diff --git a/lib/build/.rvmrc b/lib/build/.rvmrc deleted file mode 100644 index 4bb8363b30..0000000000 --- a/lib/build/.rvmrc +++ /dev/null @@ -1 +0,0 @@ -rvm use --create 1.9.2@cartodbui > /dev/null diff --git a/lib/build/files/js_files.js b/lib/build/files/js_files.js index 12a57c48a3..380659515e 100644 --- a/lib/build/files/js_files.js +++ b/lib/build/files/js_files.js @@ -3,263 +3,263 @@ var _ = require('underscore'); module.exports = files = { dashboard: [ - '../../vendor/assets/javascripts/d3.v2.js', - '../../vendor/assets/javascripts/select2.min.js', - '../../vendor/assets/javascripts/moment.js', - '../../vendor/assets/javascripts/jquery-ui/**/*.js', - '../../vendor/assets/javascripts/jquery.fileupload.js', - '../../vendor/assets/javascripts/jquery.fileupload-fp.js', - '../../vendor/assets/javascripts/jquery.fileupload-ui.js', - '../../vendor/assets/javascripts/models.js', - '../../vendor/assets/javascripts/markdown.js', - '../../vendor/assets/javascripts/datepicker.js', - '../assets/javascripts/cartodb/common/dropdown_menu.js', - '../assets/javascripts/cartodb/common/dropdown_basemap.js', - '../assets/javascripts/cartodb/common/forms/string_field.js', - '../assets/javascripts/cartodb/common/forms/widgets.js', - '../assets/javascripts/cartodb/common/import/import_pane.js', - '../assets/javascripts/cartodb/common/import/import_info/import_info.js', - '../assets/javascripts/cartodb/common/**/*.js', - '../assets/javascripts/cartodb/dashboard/**/*.js', - '../assets/javascripts/cartodb/dashboard/dashboard.js', - '../assets/javascripts/cartodb/dashboard/views/**/*.js', - '../assets/javascripts/cartodb/common/views/**/*.js', - '../assets/javascripts/cartodb/common/export_table_dialog.js', + 'vendor/assets/javascripts/d3.v2.js', + 'vendor/assets/javascripts/select2.min.js', + 'vendor/assets/javascripts/moment.js', + 'vendor/assets/javascripts/jquery-ui/**/*.js', + 'vendor/assets/javascripts/jquery.fileupload.js', + 'vendor/assets/javascripts/jquery.fileupload-fp.js', + 'vendor/assets/javascripts/jquery.fileupload-ui.js', + 'vendor/assets/javascripts/models.js', + 'vendor/assets/javascripts/markdown.js', + 'vendor/assets/javascripts/datepicker.js', + 'lib/assets/javascripts/cartodb/common/dropdown_menu.js', + 'lib/assets/javascripts/cartodb/common/dropdown_basemap.js', + 'lib/assets/javascripts/cartodb/common/forms/string_field.js', + 'lib/assets/javascripts/cartodb/common/forms/widgets.js', + 'lib/assets/javascripts/cartodb/common/import/import_pane.js', + 'lib/assets/javascripts/cartodb/common/import/import_info/import_info.js', + 'lib/assets/javascripts/cartodb/common/**/*.js', + 'lib/assets/javascripts/cartodb/dashboard/**/*.js', + 'lib/assets/javascripts/cartodb/dashboard/dashboard.js', + 'lib/assets/javascripts/cartodb/dashboard/views/**/*.js', + 'lib/assets/javascripts/cartodb/common/views/**/*.js', + 'lib/assets/javascripts/cartodb/common/export_table_dialog.js' ], application: [ - '../../vendor/assets/javascripts/jquery.tipsy.js', - '../../vendor/assets/javascripts/rails.js' + 'vendor/assets/javascripts/jquery.tipsy.js', + 'vendor/assets/javascripts/rails.js' ], cdb: [ - '../../vendor/assets/javascripts/cartodb.uncompressed.js', - '../../vendor/assets/javascripts/cartodb.mod.torque.uncompressed.js', - '../assets/javascripts/cartodb/app.js', - 'app_config.js' + 'vendor/assets/javascripts/cartodb.uncompressed.js', + 'vendor/assets/javascripts/cartodb.mod.torque.uncompressed.js', + 'lib/assets/javascripts/cartodb/app.js', + 'lib/build/app_config.js' ], app: [ - '../assets/javascripts/cartodb/app.js', - 'app_config.js' + 'lib/assets/javascripts/cartodb/app.js', + 'lib/build/app_config.js' ], common_data: [ - '../../vendor/assets/javascripts/jquery-ui/**/*.js', - '../../vendor/assets/javascripts/jquery.fileupload.js', - '../../vendor/assets/javascripts/jquery.fileupload-fp.js', - '../../vendor/assets/javascripts/jquery.fileupload-ui.js', - '../../vendor/assets/javascripts/select2.min.js', - '../../vendor/assets/javascripts/moment.js', - '../../vendor/assets/javascripts/datepicker.js', - '../../vendor/assets/javascripts/models.js', - '../../vendor/assets/javascripts/markdown.js', - '../assets/javascripts/cartodb/common/dropdown_menu.js', - '../assets/javascripts/cartodb/common/user_settings_dropdown.js', - '../assets/javascripts/cartodb/common/forms/string_field.js', - '../assets/javascripts/cartodb/common/forms/widgets.js', - '../assets/javascripts/cartodb/common/import/import_pane.js', - '../assets/javascripts/cartodb/common/import/import_info/import_info.js', - '../assets/javascripts/cartodb/common/**/*.js', - '../assets/javascripts/cartodb/common_data/**/*.js', - '../assets/javascripts/cartodb/common_data/common_data.js', - '../assets/javascripts/cartodb/dashboard/views/**/*.js', - '../assets/javascripts/cartodb/common/views/**/*.js' + 'vendor/assets/javascripts/jquery-ui/**/*.js', + 'vendor/assets/javascripts/jquery.fileupload.js', + 'vendor/assets/javascripts/jquery.fileupload-fp.js', + 'vendor/assets/javascripts/jquery.fileupload-ui.js', + 'vendor/assets/javascripts/select2.min.js', + 'vendor/assets/javascripts/moment.js', + 'vendor/assets/javascripts/datepicker.js', + 'vendor/assets/javascripts/models.js', + 'vendor/assets/javascripts/markdown.js', + 'lib/assets/javascripts/cartodb/common/dropdown_menu.js', + 'lib/assets/javascripts/cartodb/common/user_settings_dropdown.js', + 'lib/assets/javascripts/cartodb/common/forms/string_field.js', + 'lib/assets/javascripts/cartodb/common/forms/widgets.js', + 'lib/assets/javascripts/cartodb/common/import/import_pane.js', + 'lib/assets/javascripts/cartodb/common/import/import_info/import_info.js', + 'lib/assets/javascripts/cartodb/common/**/*.js', + 'lib/assets/javascripts/cartodb/common_data/**/*.js', + 'lib/assets/javascripts/cartodb/common_data/common_data.js', + 'lib/assets/javascripts/cartodb/dashboard/views/**/*.js', + 'lib/assets/javascripts/cartodb/common/views/**/*.js' ], keys: [ - '../../vendor/assets/javascripts/ZeroClipboard.js', - '../../vendor/assets/javascripts/models.js', - '../assets/javascripts/cartodb/common/forms/string_field.js', - '../assets/javascripts/cartodb/common/search_form.js', - '../assets/javascripts/cartodb/common/dropdown_menu.js', - '../assets/javascripts/cartodb/common/user_settings_dropdown.js', - '../assets/javascripts/cartodb/common/error_stats.js', - '../assets/javascripts/cartodb/keys/**/*.js', - '../assets/javascripts/cartodb/common/view/**/*.js' + 'vendor/assets/javascripts/ZeroClipboard.js', + 'vendor/assets/javascripts/models.js', + 'lib/assets/javascripts/cartodb/common/forms/string_field.js', + 'lib/assets/javascripts/cartodb/common/search_form.js', + 'lib/assets/javascripts/cartodb/common/dropdown_menu.js', + 'lib/assets/javascripts/cartodb/common/user_settings_dropdown.js', + 'lib/assets/javascripts/cartodb/common/error_stats.js', + 'lib/assets/javascripts/cartodb/keys/**/*.js', + 'lib/assets/javascripts/cartodb/common/view/**/*.js' ], login: [ - '../../vendor/assets/javascripts/modernizr-min.js', - '../../vendor/assets/javascripts/selectivizr-min.js', - '../assets/javascripts/cartodb/app.js', - '../assets/javascripts/cartodb/common/error_stats.js', - '../assets/javascripts/cartodb/login/placeholder.js', - '../assets/javascripts/cartodb/login/login.js' + 'vendor/assets/javascripts/modernizr-min.js', + 'vendor/assets/javascripts/selectivizr-min.js', + 'lib/assets/javascripts/cartodb/app.js', + 'lib/assets/javascripts/cartodb/common/error_stats.js', + 'lib/assets/javascripts/cartodb/login/placeholder.js', + 'lib/assets/javascripts/cartodb/login/login.js' ], models: [ - '../assets/javascripts/cartodb/models/table.js', - '../assets/javascripts/cartodb/models/tabledata.js', - '../assets/javascripts/cartodb/models/sqlview.js', - '../assets/javascripts/cartodb/models/cartodb_layer.js', - '../assets/javascripts/cartodb/models/map.js', - '../assets/javascripts/cartodb/models/user.js', - '../assets/javascripts/cartodb/models/organization.js', - '../assets/javascripts/cartodb/models/carto/*.js', - '../assets/javascripts/cartodb/models/**/*.js' + 'lib/assets/javascripts/cartodb/models/table.js', + 'lib/assets/javascripts/cartodb/models/tabledata.js', + 'lib/assets/javascripts/cartodb/models/sqlview.js', + 'lib/assets/javascripts/cartodb/models/cartodb_layer.js', + 'lib/assets/javascripts/cartodb/models/map.js', + 'lib/assets/javascripts/cartodb/models/user.js', + 'lib/assets/javascripts/cartodb/models/organization.js', + 'lib/assets/javascripts/cartodb/models/carto/*.js', + 'lib/assets/javascripts/cartodb/models/**/*.js' ], organization: [ - '../assets/javascripts/cartodb/common/forms/widgets.js', - '../assets/javascripts/cartodb/common/dropdown_menu.js', - '../assets/javascripts/cartodb/common/user_settings_dropdown.js', - '../assets/javascripts/cartodb/common/base_dialog.js', - '../assets/javascripts/cartodb/common/utils.js', - '../assets/javascripts/cartodb/common/global_click.js', - '../assets/javascripts/cartodb/common/error_stats.js', - '../assets/javascripts/cartodb/common/views/rollbar.js', - '../assets/javascripts/cartodb/common/views/settings_item.js', - '../assets/javascripts/cartodb/common/views/confirm_dialog.js', - '../assets/javascripts/cartodb/common/views/dialog_base.js', - '../../vendor/assets/javascripts/filestyle.js', - '../assets/javascripts/cartodb/organization/**/*.js' + 'lib/assets/javascripts/cartodb/common/forms/widgets.js', + 'lib/assets/javascripts/cartodb/common/dropdown_menu.js', + 'lib/assets/javascripts/cartodb/common/user_settings_dropdown.js', + 'lib/assets/javascripts/cartodb/common/base_dialog.js', + 'lib/assets/javascripts/cartodb/common/utils.js', + 'lib/assets/javascripts/cartodb/common/global_click.js', + 'lib/assets/javascripts/cartodb/common/error_stats.js', + 'lib/assets/javascripts/cartodb/common/views/rollbar.js', + 'lib/assets/javascripts/cartodb/common/views/settings_item.js', + 'lib/assets/javascripts/cartodb/common/views/confirm_dialog.js', + 'lib/assets/javascripts/cartodb/common/views/dialog_base.js', + 'vendor/assets/javascripts/filestyle.js', + 'lib/assets/javascripts/cartodb/organization/**/*.js' ], specs: [ /*'test/lib/jasmine-1.3.1/jasmine.js', 'test/lib/jasmine-1.3.1/jasmine-html.js',*/ - '../assets/test/spec/cartodb/**/*.js' + 'lib/assets/test/spec/cartodb/**/*.js' ], _spec_helpers: [ - //'../assets/test/lib/jasmine.jquery.js', - '../assets/test/lib/sinon-1.3.4.js', - '../assets/test/spec/SpecHelper.js', + //'lib/assets/test/lib/jasmine.jquery.js', + 'lib/assets/test/lib/sinon-1.3.4.js', + 'lib/assets/test/spec/SpecHelper.js' ], table: [ - '../../vendor/assets/javascripts/codemirror.js', - '../../vendor/assets/javascripts/show-hint.js', - '../../vendor/assets/javascripts/anyword-hint.js', - '../../vendor/assets/javascripts/custom-list-hint.js', - '../../vendor/assets/javascripts/custom-list-with-type-hint.js', - '../../vendor/assets/javascripts/select2.min.js', - '../../vendor/assets/javascripts/jquery.faviconNotify.js', - '../../vendor/assets/javascripts/rgbcolor.js', - '../../vendor/assets/javascripts/crossfilter.js', - '../../vendor/assets/javascripts/jquery-ui/**/*.js', - '../../vendor/assets/javascripts/jquery.caret.js', - '../../vendor/assets/javascripts/ZeroClipboard.js', - '../../vendor/assets/javascripts/tag-it.js', - '../../vendor/assets/javascripts/jquery.tipsy.js', - '../../vendor/assets/javascripts/d3.v2.js', - '../../vendor/assets/javascripts/colorpicker.js', - '../../vendor/assets/javascripts/jquery.fileupload.js', - '../../vendor/assets/javascripts/jquery.fileupload-fp.js', - '../../vendor/assets/javascripts/jquery.fileupload-ui.js', - '../../vendor/assets/javascripts/leaflet.draw.js', - '../../vendor/assets/javascripts/moment.js', - '../../vendor/assets/javascripts/markdown.js', - '../../vendor/assets/javascripts/datepicker.js', - '../assets/javascripts/utils/postgres.codemirror.js', - '../assets/javascripts/utils/xml.codemirror.js', - '../assets/javascripts/utils/draggable.js', - '../assets/javascripts/utils/carto.codemirror.js', - '../assets/javascripts/utils/color.keywords.js', - '../../vendor/assets/javascripts/models.js', - '../assets/javascripts/cartodb/common/dropdown_menu.js', - '../assets/javascripts/cartodb/common/forms/string_field.js', - '../assets/javascripts/cartodb/common/forms/widgets.js', - '../assets/javascripts/cartodb/table/overlays/overlays.js', - '../assets/javascripts/cartodb/table/overlays/text.js', - '../assets/javascripts/cartodb/common/import/import_pane.js', - '../assets/javascripts/cartodb/common/import/import_info/import_info.js', - '../assets/javascripts/cartodb/common/**/*.js', - '../assets/javascripts/cartodb/table/right_menu.js', - '../assets/javascripts/cartodb/table/default_layers.js', - '../assets/javascripts/cartodb/table/menu_module.js', - '../assets/javascripts/cartodb/table/menu_modules/carto_editor.js', - '../assets/javascripts/cartodb/table/menu_modules/carto_wizard.js', - '../assets/javascripts/cartodb/table/**/*.js', - '../assets/javascripts/cartodb/table/table.js', - '../assets/javascripts/cartodb/table/views/**/*.js', - '../assets/javascripts/cartodb/dashboard/views/**/*.js' + 'vendor/assets/javascripts/codemirror.js', + 'vendor/assets/javascripts/show-hint.js', + 'vendor/assets/javascripts/anyword-hint.js', + 'vendor/assets/javascripts/custom-list-hint.js', + 'vendor/assets/javascripts/custom-list-with-type-hint.js', + 'vendor/assets/javascripts/select2.min.js', + 'vendor/assets/javascripts/jquery.faviconNotify.js', + 'vendor/assets/javascripts/rgbcolor.js', + 'vendor/assets/javascripts/crossfilter.js', + 'vendor/assets/javascripts/jquery-ui/**/*.js', + 'vendor/assets/javascripts/jquery.caret.js', + 'vendor/assets/javascripts/ZeroClipboard.js', + 'vendor/assets/javascripts/tag-it.js', + 'vendor/assets/javascripts/jquery.tipsy.js', + 'vendor/assets/javascripts/d3.v2.js', + 'vendor/assets/javascripts/colorpicker.js', + 'vendor/assets/javascripts/jquery.fileupload.js', + 'vendor/assets/javascripts/jquery.fileupload-fp.js', + 'vendor/assets/javascripts/jquery.fileupload-ui.js', + 'vendor/assets/javascripts/leaflet.draw.js', + 'vendor/assets/javascripts/moment.js', + 'vendor/assets/javascripts/markdown.js', + 'vendor/assets/javascripts/datepicker.js', + 'lib/assets/javascripts/utils/postgres.codemirror.js', + 'lib/assets/javascripts/utils/xml.codemirror.js', + 'lib/assets/javascripts/utils/draggable.js', + 'lib/assets/javascripts/utils/carto.codemirror.js', + 'lib/assets/javascripts/utils/color.keywords.js', + 'vendor/assets/javascripts/models.js', + 'lib/assets/javascripts/cartodb/common/dropdown_menu.js', + 'lib/assets/javascripts/cartodb/common/forms/string_field.js', + 'lib/assets/javascripts/cartodb/common/forms/widgets.js', + 'lib/assets/javascripts/cartodb/table/overlays/overlays.js', + 'lib/assets/javascripts/cartodb/table/overlays/text.js', + 'lib/assets/javascripts/cartodb/common/import/import_pane.js', + 'lib/assets/javascripts/cartodb/common/import/import_info/import_info.js', + 'lib/assets/javascripts/cartodb/common/**/*.js', + 'lib/assets/javascripts/cartodb/table/right_menu.js', + 'lib/assets/javascripts/cartodb/table/default_layers.js', + 'lib/assets/javascripts/cartodb/table/menu_module.js', + 'lib/assets/javascripts/cartodb/table/menu_modules/carto_editor.js', + 'lib/assets/javascripts/cartodb/table/menu_modules/carto_wizard.js', + 'lib/assets/javascripts/cartodb/table/**/*.js', + 'lib/assets/javascripts/cartodb/table/table.js', + 'lib/assets/javascripts/cartodb/table/views/**/*.js', + 'lib/assets/javascripts/cartodb/dashboard/views/**/*.js' ], public_dashboard: [ - '../../vendor/assets/javascripts/jquery.tipsy.js', - '../assets/javascripts/cartodb/common/dropdown_menu.js', - '../assets/javascripts/cartodb/public/**/*.js', - '../assets/javascripts/cartodb/public_dashboard/*.js' + 'vendor/assets/javascripts/jquery.tipsy.js', + 'lib/assets/javascripts/cartodb/common/dropdown_menu.js', + 'lib/assets/javascripts/cartodb/public/**/*.js', + 'lib/assets/javascripts/cartodb/public_dashboard/*.js' ], public_table: [ // Vendor - '../../vendor/assets/javascripts/modernizr-min.js', - '../../vendor/assets/javascripts/jquery.tipsy.js', - '../../vendor/assets/javascripts/select2.min.js', + 'vendor/assets/javascripts/modernizr-min.js', + 'vendor/assets/javascripts/jquery.tipsy.js', + 'vendor/assets/javascripts/select2.min.js', // Models - '../assets/javascripts/cartodb/models/table.js', - '../assets/javascripts/cartodb/models/tabledata.js', - '../assets/javascripts/cartodb/models/sqlview.js', - '../assets/javascripts/cartodb/models/cartodb_layer.js', - '../assets/javascripts/cartodb/models/map.js', - '../assets/javascripts/cartodb/models/user.js', - '../assets/javascripts/cartodb/models/permissions.js', - '../assets/javascripts/cartodb/models/organization.js', - '../assets/javascripts/cartodb/models/synchronization.js', - '../assets/javascripts/cartodb/models/wkt.js', - '../assets/javascripts/cartodb/models/vis.js', + 'lib/assets/javascripts/cartodb/models/table.js', + 'lib/assets/javascripts/cartodb/models/tabledata.js', + 'lib/assets/javascripts/cartodb/models/sqlview.js', + 'lib/assets/javascripts/cartodb/models/cartodb_layer.js', + 'lib/assets/javascripts/cartodb/models/map.js', + 'lib/assets/javascripts/cartodb/models/user.js', + 'lib/assets/javascripts/cartodb/models/permissions.js', + 'lib/assets/javascripts/cartodb/models/organization.js', + 'lib/assets/javascripts/cartodb/models/synchronization.js', + 'lib/assets/javascripts/cartodb/models/wkt.js', + 'lib/assets/javascripts/cartodb/models/vis.js', // Tabpane from CDB.js :S - '../assets/javascripts/cdb/src/ui/common/tabpane.js', + 'lib/assets/javascripts/cdb/src/ui/common/tabpane.js', // UI - '../assets/javascripts/cartodb/common/dropdown_menu.js', - '../assets/javascripts/cartodb/common/forms/string_field.js', - '../assets/javascripts/cartodb/common/forms/widgets.js', - '../assets/javascripts/cartodb/common/import/import_pane.js', - '../assets/javascripts/cartodb/common/import/import_info/import_info.js', - '../assets/javascripts/cartodb/common/**/*.js', - '../assets/javascripts/cartodb/common/export_table_dialog.js', - '../assets/javascripts/cartodb/table/editor_small_dialog.js', - - '../assets/javascripts/cartodb/table/header/duplicate_table_dialog.js', - '../assets/javascripts/cartodb/table/infowindow.js', - '../assets/javascripts/cartodb/table/tooltip.js', - '../assets/javascripts/cartodb/table/row_view.js', - '../assets/javascripts/cartodb/table/header_view.js', - '../assets/javascripts/cartodb/table/tableview.js', - '../assets/javascripts/cartodb/table/mapview.js', - '../assets/javascripts/cartodb/table/header_dropdown.js', + 'lib/assets/javascripts/cartodb/common/dropdown_menu.js', + 'lib/assets/javascripts/cartodb/common/forms/string_field.js', + 'lib/assets/javascripts/cartodb/common/forms/widgets.js', + 'lib/assets/javascripts/cartodb/common/import/import_pane.js', + 'lib/assets/javascripts/cartodb/common/import/import_info/import_info.js', + 'lib/assets/javascripts/cartodb/common/**/*.js', + 'lib/assets/javascripts/cartodb/common/export_table_dialog.js', + 'lib/assets/javascripts/cartodb/table/editor_small_dialog.js', + + 'lib/assets/javascripts/cartodb/table/header/duplicate_table_dialog.js', + 'lib/assets/javascripts/cartodb/table/infowindow.js', + 'lib/assets/javascripts/cartodb/table/tooltip.js', + 'lib/assets/javascripts/cartodb/table/row_view.js', + 'lib/assets/javascripts/cartodb/table/header_view.js', + 'lib/assets/javascripts/cartodb/table/tableview.js', + 'lib/assets/javascripts/cartodb/table/mapview.js', + 'lib/assets/javascripts/cartodb/table/header_dropdown.js', // Public UI - '../assets/javascripts/cartodb/public/**/*.js', - '../assets/javascripts/cartodb/public_table/**/*.js', + 'lib/assets/javascripts/cartodb/public/**/*.js', + 'lib/assets/javascripts/cartodb/public_table/**/*.js', - '../assets/javascripts/cartodb/public_table/public_table.js' + 'lib/assets/javascripts/cartodb/public_table/public_table.js' ], public_map: [ - '../../vendor/assets/javascripts/jquery.tipsy.js', - '../assets/javascripts/cartodb/common/dropdown_menu.js', - '../assets/javascripts/cartodb/common/base_dialog.js', - '../assets/javascripts/cartodb/table/header/duplicate_visualization_dialog.js', - '../assets/javascripts/cartodb/public/**/*.js', - '../assets/javascripts/cartodb/public_map/**/*.js' + 'vendor/assets/javascripts/jquery.tipsy.js', + 'lib/assets/javascripts/cartodb/common/dropdown_menu.js', + 'lib/assets/javascripts/cartodb/common/base_dialog.js', + 'lib/assets/javascripts/cartodb/table/header/duplicate_visualization_dialog.js', + 'lib/assets/javascripts/cartodb/public/**/*.js', + 'lib/assets/javascripts/cartodb/public_map/**/*.js' ], tipsy: [ - '../../vendor/assets/javascripts/jquery.tipsy.js' + 'vendor/assets/javascripts/jquery.tipsy.js' ], modernizr: [ - '../../vendor/assets/javascripts/modernizr-min.js', + 'vendor/assets/javascripts/modernizr-min.js' ], statsc: [ - '../../vendor/assets/javascripts/statsc.min.js' + 'vendor/assets/javascripts/statsc.min.js' ], _templates: [ - '../assets/javascripts/cartodb/**/*.jst.ejs' + 'lib/assets/javascripts/cartodb/**/*.jst.ejs' ], _templates_mustache: [ - '../assets/javascripts/cartodb/**/*.jst.mustache' + 'lib/assets/javascripts/cartodb/**/*.jst.mustache' ] }; diff --git a/lib/build/tasks/browserify.js b/lib/build/tasks/browserify.js index a13e299923..7df138de1a 100644 --- a/lib/build/tasks/browserify.js +++ b/lib/build/tasks/browserify.js @@ -8,20 +8,24 @@ module.exports = { preBundleCB: function (b) { b.plugin(remapify, [ { - cwd: './src', + cwd: './lib/assets/javascripts/cartodb', + src: './**/*.js', + expose: 'cartodb' + }, { + cwd: './lib/assets/javascripts/cartodb2', src: './**/*.js', expose: '' } ]); } }, - dashboard: { - src: 'src/dashboard.js', + cartodb2: { + src: 'lib/assets/javascripts/cartodb2/dashboard.js', dest: '<%= assets_dir %>/javascripts/new-dashboard.js' }, - test: { - src: ['test/spec/**/*.spec.js'], - dest: './test/test_bundle.js', + cartodb2_tests: { + src: 'lib/assets/test/spec/cartodb2/**/*.spec.js', + dest: 'lib/assets/test/cartodb2_tests.js', options: { browserifyOptions: { debug: true diff --git a/lib/build/tasks/clean.js b/lib/build/tasks/clean.js index adf3859fc9..5564ccb2d5 100644 --- a/lib/build/tasks/clean.js +++ b/lib/build/tasks/clean.js @@ -10,9 +10,9 @@ force: true }, src: [ - "app_config.js", - "./.sass-cache", - "../../app/assets/stylesheets/tmp", + "lib/build/app_config.js", + ".sass-cache", + "app/assets/stylesheets/tmp", "<%= assets_dir %>" ] } diff --git a/lib/build/tasks/compass.js b/lib/build/tasks/compass.js index 6a4c666296..e8783538bd 100644 --- a/lib/build/tasks/compass.js +++ b/lib/build/tasks/compass.js @@ -8,15 +8,15 @@ return { dist: { options: { - importPath: '../../app/assets/stylesheets/tmp/common', + importPath: 'app/assets/stylesheets/tmp/common', - sassDir: '../../app/assets/stylesheets/tmp', + sassDir: 'app/assets/stylesheets/tmp', cssDir: '<%= assets_dir %>/stylesheets', fontsDir: '<%= assets_dir %>/fonts', httpFontsPath: '<%= env.http_path_prefix %>/assets/<%= pkg.version %>/fonts', - imagesDir: '../../app/assets/images/', + imagesDir: 'app/assets/images/', generatedImagesDir: '<%= assets_dir %>/images/', httpImagesPath: '<%= env.http_path_prefix %>/assets/<%= pkg.version %>/images/', httpGeneratedImagesPath: '<%= env.http_path_prefix %>/assets/<%= pkg.version %>/images/', diff --git a/lib/build/tasks/copy.js b/lib/build/tasks/copy.js index 1e855689fe..10804234b1 100644 --- a/lib/build/tasks/copy.js +++ b/lib/build/tasks/copy.js @@ -11,9 +11,9 @@ vendor: { expand: true, - cwd: '../../vendor/assets/stylesheets/', + cwd: 'vendor/assets/stylesheets/', src: ['**/*.css'], - dest: '../../app/assets/stylesheets/tmp/vendor/', + dest: 'app/assets/stylesheets/tmp/vendor/', rename: function(dest, src) { return dest + src.replace(/\.css$/, ".scss"); }, @@ -38,9 +38,9 @@ // App stylesheets { expand: true, - cwd: '../../app/assets/stylesheets/', + cwd: 'app/assets/stylesheets/', src: ['**/*.css.scss'], - dest: '../../app/assets/stylesheets/tmp/', + dest: 'app/assets/stylesheets/tmp/', rename: function(dest, src) { return dest + src.replace(/\.css.scss$/, ".scss"); } @@ -49,9 +49,9 @@ // Jasmine stylesheets { expand: true, - cwd: '../../lib/assets/test/lib/jasmine-1.3.1/', + cwd: 'lib/assets/test/lib/jasmine-1.3.1/', src: ['**/*.css'], - dest: '../../app/assets/stylesheets/tmp/specs/', + dest: 'app/assets/stylesheets/tmp/specs/', rename: function(dest, src) { return dest + src.replace(/\.css$/, ".scss"); } @@ -60,7 +60,7 @@ // Embed stylesheets { expand: true, - cwd: '../../lib/assets/javascripts/cdb/themes/css/', + cwd: 'lib/assets/javascripts/cdb/themes/css/', src: ['cartodb.css'], dest: '<%= assets_dir %>/stylesheets/tmp/embeds/', rename: function(dest, src) { @@ -76,7 +76,7 @@ { expand: true, - cwd: '../../app/assets/images/', + cwd: 'app/assets/images/', src: ['**/*'], dest: '<%= assets_dir %>/images/' }, @@ -85,7 +85,7 @@ { expand: true, - cwd: '../../lib/assets/javascripts/cdb/themes/img/', + cwd: 'lib/assets/javascripts/cdb/themes/img/', src: ['**/*'], dest: '<%= assets_dir %>/images/themes/' }, @@ -96,7 +96,7 @@ { expand: true, - cwd: '../../app/assets/fonts/', + cwd: 'app/assets/fonts/', src: ['**/*'], dest: '<%= assets_dir %>/fonts/' }, @@ -107,7 +107,7 @@ { expand: true, - cwd: '../../app/assets/flash/', + cwd: 'app/assets/flash/', src: ['**/*'], dest: '<%= assets_dir %>/flash/' }, @@ -118,7 +118,7 @@ { expand: true, - cwd: '../../public/favicons/', + cwd: 'public/favicons/', src: ['**/*'], dest: '<%= assets_dir %>/favicons/' } diff --git a/lib/build/tasks/exorcise.js b/lib/build/tasks/exorcise.js deleted file mode 100644 index 29b036c07e..0000000000 --- a/lib/build/tasks/exorcise.js +++ /dev/null @@ -1,13 +0,0 @@ -// Source maps -module.exports = { - task: function() { - return { - test: { - options: {}, - files: { - 'test/test_bundle.map': ['test/test_bundle.js'] - } - } - } - } -}; diff --git a/lib/build/tasks/jasmine.js b/lib/build/tasks/jasmine.js index 9601666312..bd98979340 100644 --- a/lib/build/tasks/jasmine.js +++ b/lib/build/tasks/jasmine.js @@ -9,10 +9,10 @@ return { cartodbui: { src: js_files.all.concat([ - 'user_data.js', - '<%= assets_dir %>/javascripts/templates_mustache.js', + 'lib/build/user_data.js', + '<%= assets_dir %>/javascripts/templates_mustache.js', '<%= assets_dir %>/javascripts/templates.js', - 'test_init.js']), + 'lib/build/test_init.js']), options: { summary: true, display: 'short', @@ -21,14 +21,14 @@ //'--remote-debugger-port': 9000 } }, - new_dashboard: { + cartodbui2: { // src and options.specs are created by the browserify task. src: '<%= assets_dir %>/javascripts/new-dashboard.js', options: { summary: true, display: 'short', - specs: './test/test_bundle.js', - keepRunner: true + specs: './lib/assets/test/cartodb2_tests.js', + keepRunner: true // do not delete _SpecRunner.html, added in .gitignore so won't be versioned anyway. } } } diff --git a/lib/build/tasks/jst.js b/lib/build/tasks/jst.js index ae23c64c1d..37438924fb 100644 --- a/lib/build/tasks/jst.js +++ b/lib/build/tasks/jst.js @@ -11,7 +11,7 @@ compile: { options: { processName: function(filename) { - return filename.replace(/^\.\.\/assets\/javascripts\//, '').replace(/\.jst\.ejs$/, ''); + return filename.replace(/^lib\/assets\/javascripts\//, '').replace(/\.jst\.ejs$/, ''); } }, files: { @@ -21,15 +21,15 @@ mustache: { options: { processName: function(filename) { - return filename.replace(/^..\/assets\/javascripts\//, '').replace(/\.jst\.mustache/, ''); + return filename.replace(/^lib\/assets\/javascripts\//, '').replace(/\.jst\.mustache/, ''); }, template: function(source) { - var src = source.replace(/\n/g, '\\n').replace(/'/g,"\\'") + var src = source.replace(/\n/g, '\\n').replace(/'/g,"\\'"); return { source: "Mustache.compile('"+ src +"')" } } }, files: { - '<%= assets_dir %>/javascripts/templates_mustache.js': js_files._templates_mustache, + '<%= assets_dir %>/javascripts/templates_mustache.js': js_files._templates_mustache } } } diff --git a/lib/build/tasks/watch.js b/lib/build/tasks/watch.js index 9108066e23..f8b77420f6 100644 --- a/lib/build/tasks/watch.js +++ b/lib/build/tasks/watch.js @@ -11,7 +11,7 @@ exports.task = function() { } // watch cdb files - js.push(['../assets/javascripts/cdb/src/**/*.js']); + js.push(['lib/assets/javascripts/cdb/src/**/*.js']); return { js: { @@ -23,8 +23,8 @@ exports.task = function() { }, css: { files: [ - '../../vendor/assets/stylesheets/**/*.css.scss', - '../../app/assets/stylesheets/**/*.css.scss' + 'assets/stylesheets/**/*.css.scss', + 'app/assets/stylesheets/**/*.css.scss' ], tasks: ['css'], options: { @@ -33,19 +33,19 @@ exports.task = function() { }, livereload: { files: [ - '../../public/assets/<%= pkg.version %>/stylesheets/*.css', - '../../public/assets/<%= pkg.version %>/javascripts/*.js' + 'public/assets/<%= pkg.version %>/stylesheets/*.css', + 'public/assets/<%= pkg.version %>/javascripts/*.js' ], options: { livereload: true } }, - new_dashboard: { + cartodb2: { files: [ - 'src/**/*.js', - 'test/**/*.js' + 'lib/assets/javascripts/cartodb2/**/*.js', + 'lib/assets/test/cartodb2/**/*.js' ], - tasks: ['browserify', 'exorcise', 'jasmine:new_dashboard'], + tasks: ['browserify', 'jasmine:cartodbui2'], options: { atBegin: true } diff --git a/lib/build/package.json b/package.json similarity index 97% rename from lib/build/package.json rename to package.json index 9becb3ff25..a6534ffa5a 100644 --- a/lib/build/package.json +++ b/package.json @@ -36,7 +36,6 @@ "grunt-contrib-sass": "~0.7.3", "grunt-contrib-uglify": "~0.4.0", "grunt-contrib-watch": "~0.6.0", - "grunt-exorcise": "^0.2.0", "grunt-mustache": "~0.1.6", "grunt-timer": "~0.3.3", "load-grunt-tasks": "~0.2.0", diff --git a/test/test_bundle.map b/test/test_bundle.map new file mode 100644 index 0000000000..1b4c08221f --- /dev/null +++ b/test/test_bundle.map @@ -0,0 +1,17 @@ +{ + "version": 3, + "sources": [ + "node_modules/browserify/node_modules/browser-pack/_prelude.js", + "lib/assets/javascripts/cartodb2/dashboard/hello_world.js", + "lib/assets/test/spec/cartodb2/dashboard/hello_world.spec.js" + ], + "names": [], + "mappings": "AAAA;ACAA;AACA;AACA;AACA;;ACHA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA", + "file": "generated.js", + "sourceRoot": "", + "sourcesContent": [ + "(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require==\"function\"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error(\"Cannot find module '\"+o+\"'\");throw f.code=\"MODULE_NOT_FOUND\",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require==\"function\"&&require;for(var o=0;o= minzoom - 1e-6 and scale < maxzoom + 1e-6" - }, - "maxzoom": { - "default-value": "1.79769e+308", - "type":"float", - "default-meaning": "The layer will be visible at the maximum possible scale", - "doc": "The maximum scale denominator that this layer will be visible at. The default is the numeric limit of the C++ double type, which may vary slightly by system, but is likely a massive number like 1.79769e+308 and ensures that this layer will always be visible unless the value is reduced. A layer's visibility is determined by whether its status is true and if the Map scale >= minzoom - 1e-6 and scale < maxzoom + 1e-6" - }, - "queryable": { - "default-value": false, - "type":"boolean", - "default-meaning": "The layer will not be available for the direct querying of data values", - "doc": "This property was added for GetFeatureInfo/WMS compatibility and is rarely used. It is off by default meaning that in a WMS context the layer will not be able to be queried unless the property is explicitly set to true" - }, - "clear-label-cache": { - "default-value": false, - "type":"boolean", - "default-meaning": "The renderer's collision detector cache (used for avoiding duplicate labels and overlapping markers) will not be cleared immediately before processing this layer", - "doc": "This property, by default off, can be enabled to allow a user to clear the collision detector cache before a given layer is processed. This may be desirable to ensure that a given layers data shows up on the map even if it normally would not because of collisions with previously rendered labels or markers" - }, - "group-by": { - "default-value": "", - "type":"string", - "default-meaning": "No special layer grouping will be used during rendering", - "doc": "https://github.com/mapnik/mapnik/wiki/Grouped-rendering" - }, - "buffer-size": { - "default-value": "0", - "type":"float", - "default-meaning": "No buffer will be used", - "doc": "Extra tolerance around the Layer extent (in pixels) used to when querying and (potentially) clipping the layer data during rendering" - }, - "maximum-extent": { - "default-value": "none", - "type":"bbox", - "default-meaning": "No clipping extent will be used", - "doc": "An extent to be used to limit the bounds used to query this specific layer data during rendering. Should be minx, miny, maxx, maxy in the coordinates of the Layer." - } - }, - "symbolizers" : { - "*": { - "image-filters": { - "css": "image-filters", - "default-value": "none", - "default-meaning": "no filters", - "type": "functions", - "functions": [ - ["agg-stack-blur", 2], - ["emboss", 0], - ["blur", 0], - ["gray", 0], - ["sobel", 0], - ["edge-detect", 0], - ["x-gradient", 0], - ["y-gradient", 0], - ["invert", 0], - ["sharpen", 0] - ], - "doc": "A list of image filters." - }, - "comp-op": { - "css": "comp-op", - "default-value": "src-over", - "default-meaning": "add the current layer on top of other layers", - "doc": "Composite operation. This defines how this layer should behave relative to layers atop or below it.", - "type": ["clear", - "src", - "dst", - "src-over", - "source-over", // added for torque - "dst-over", - "src-in", - "dst-in", - "src-out", - "dst-out", - "src-atop", - "dst-atop", - "xor", - "plus", - "minus", - "multiply", - "screen", - "overlay", - "darken", - "lighten", - "lighter", // added for torque - "color-dodge", - "color-burn", - "hard-light", - "soft-light", - "difference", - "exclusion", - "contrast", - "invert", - "invert-rgb", - "grain-merge", - "grain-extract", - "hue", - "saturation", - "color", - "value" - ] - }, - "opacity": { - "css": "opacity", - "type": "float", - "doc": "An alpha value for the style (which means an alpha applied to all features in separate buffer and then composited back to main buffer)", - "default-value": 1, - "default-meaning": "no separate buffer will be used and no alpha will be applied to the style after rendering" - } - }, - "map": { - "background-color": { - "css": "background-color", - "default-value": "none", - "default-meaning": "transparent", - "type": "color", - "doc": "Map Background color" - }, - "background-image": { - "css": "background-image", - "type": "uri", - "default-value": "", - "default-meaning": "transparent", - "doc": "An image that is repeated below all features on a map as a background.", - "description": "Map Background image" - }, - "srs": { - "css": "srs", - "type": "string", - "default-value": "+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs", - "default-meaning": "The proj4 literal of EPSG:4326 is assumed to be the Map's spatial reference and all data from layers within this map will be plotted using this coordinate system. If any layers do not declare an srs value then they will be assumed to be in the same srs as the Map and not transformations will be needed to plot them in the Map's coordinate space", - "doc": "Map spatial reference (proj4 string)" - }, - "buffer-size": { - "css": "buffer-size", - "default-value": "0", - "type":"float", - "default-meaning": "No buffer will be used", - "doc": "Extra tolerance around the map (in pixels) used to ensure labels crossing tile boundaries are equally rendered in each tile (e.g. cut in each tile). Not intended to be used in combination with \"avoid-edges\"." - }, - "maximum-extent": { - "css": "", - "default-value": "none", - "type":"bbox", - "default-meaning": "No clipping extent will be used", - "doc": "An extent to be used to limit the bounds used to query all layers during rendering. Should be minx, miny, maxx, maxy in the coordinates of the Map." - }, - "base": { - "css": "base", - "default-value": "", - "default-meaning": "This base path defaults to an empty string meaning that any relative paths to files referenced in styles or layers will be interpreted relative to the application process.", - "type": "string", - "doc": "Any relative paths used to reference files will be understood as relative to this directory path if the map is loaded from an in memory object rather than from the filesystem. If the map is loaded from the filesystem and this option is not provided it will be set to the directory of the stylesheet." - }, - "paths-from-xml": { - "css": "", - "default-value": true, - "default-meaning": "Paths read from XML will be interpreted from the location of the XML", - "type": "boolean", - "doc": "value to control whether paths in the XML will be interpreted from the location of the XML or from the working directory of the program that calls load_map()" - }, - "minimum-version": { - "css": "", - "default-value": "none", - "default-meaning": "Mapnik version will not be detected and no error will be thrown about compatibility", - "type": "string", - "doc": "The minumum Mapnik version (e.g. 0.7.2) needed to use certain functionality in the stylesheet" - }, - "font-directory": { - "css": "font-directory", - "type": "uri", - "default-value": "none", - "default-meaning": "No map-specific fonts will be registered", - "doc": "Path to a directory which holds fonts which should be registered when the Map is loaded (in addition to any fonts that may be automatically registered)." - } - }, - "polygon": { - "fill": { - "css": "polygon-fill", - "type": "color", - "default-value": "rgba(128,128,128,1)", - "default-meaning": "gray and fully opaque (alpha = 1), same as rgb(128,128,128)", - "doc": "Fill color to assign to a polygon" - }, - "fill-opacity": { - "css": "polygon-opacity", - "type": "float", - "doc": "The opacity of the polygon", - "default-value": 1, - "default-meaning": "opaque" - }, - "gamma": { - "css": "polygon-gamma", - "type": "float", - "default-value": 1, - "default-meaning": "fully antialiased", - "range": "0-1", - "doc": "Level of antialiasing of polygon edges" - }, - "gamma-method": { - "css": "polygon-gamma-method", - "type": [ - "power", - "linear", - "none", - "threshold", - "multiply" - ], - "default-value": "power", - "default-meaning": "pow(x,gamma) is used to calculate pixel gamma, which produces slightly smoother line and polygon antialiasing than the 'linear' method, while other methods are usually only used to disable AA", - "doc": "An Antigrain Geometry specific rendering hint to control the quality of antialiasing. Under the hood in Mapnik this method is used in combination with the 'gamma' value (which defaults to 1). The methods are in the AGG source at https://github.com/mapnik/mapnik/blob/master/deps/agg/include/agg_gamma_functions.h" - }, - "clip": { - "css": "polygon-clip", - "type": "boolean", - "default-value": true, - "default-meaning": "geometry will be clipped to map bounds before rendering", - "doc": "geometries are clipped to map bounds by default for best rendering performance. In some cases users may wish to disable this to avoid rendering artifacts." - }, - "smooth": { - "css": "polygon-smooth", - "type": "float", - "default-value": 0, - "default-meaning": "no smoothing", - "range": "0-1", - "doc": "Smooths out geometry angles. 0 is no smoothing, 1 is fully smoothed. Values greater than 1 will produce wild, looping geometries." - }, - "geometry-transform": { - "css": "polygon-geometry-transform", - "type": "functions", - "default-value": "none", - "default-meaning": "geometry will not be transformed", - "doc": "Allows transformation functions to be applied to the geometry.", - "functions": [ - ["matrix", 6], - ["translate", 2], - ["scale", 2], - ["rotate", 3], - ["skewX", 1], - ["skewY", 1] - ] - }, - "comp-op": { - "css": "polygon-comp-op", - "default-value": "src-over", - "default-meaning": "add the current symbolizer on top of other symbolizer", - "doc": "Composite operation. This defines how this symbolizer should behave relative to symbolizers atop or below it.", - "type": ["clear", - "src", - "dst", - "src-over", - "dst-over", - "src-in", - "dst-in", - "src-out", - "dst-out", - "src-atop", - "dst-atop", - "xor", - "plus", - "minus", - "multiply", - "screen", - "overlay", - "darken", - "lighten", - "color-dodge", - "color-burn", - "hard-light", - "soft-light", - "difference", - "exclusion", - "contrast", - "invert", - "invert-rgb", - "grain-merge", - "grain-extract", - "hue", - "saturation", - "color", - "value" - ] - } - }, - "line": { - "stroke": { - "css": "line-color", - "default-value": "rgba(0,0,0,1)", - "type": "color", - "default-meaning": "black and fully opaque (alpha = 1), same as rgb(0,0,0)", - "doc": "The color of a drawn line" - }, - "stroke-width": { - "css": "line-width", - "default-value": 1, - "type": "float", - "doc": "The width of a line in pixels" - }, - "stroke-opacity": { - "css": "line-opacity", - "default-value": 1, - "type": "float", - "default-meaning": "opaque", - "doc": "The opacity of a line" - }, - "stroke-linejoin": { - "css": "line-join", - "default-value": "miter", - "type": [ - "miter", - "round", - "bevel" - ], - "doc": "The behavior of lines when joining" - }, - "stroke-linecap": { - "css": "line-cap", - "default-value": "butt", - "type": [ - "butt", - "round", - "square" - ], - "doc": "The display of line endings" - }, - "stroke-gamma": { - "css": "line-gamma", - "type": "float", - "default-value": 1, - "default-meaning": "fully antialiased", - "range": "0-1", - "doc": "Level of antialiasing of stroke line" - }, - "stroke-gamma-method": { - "css": "line-gamma-method", - "type": [ - "power", - "linear", - "none", - "threshold", - "multiply" - ], - "default-value": "power", - "default-meaning": "pow(x,gamma) is used to calculate pixel gamma, which produces slightly smoother line and polygon antialiasing than the 'linear' method, while other methods are usually only used to disable AA", - "doc": "An Antigrain Geometry specific rendering hint to control the quality of antialiasing. Under the hood in Mapnik this method is used in combination with the 'gamma' value (which defaults to 1). The methods are in the AGG source at https://github.com/mapnik/mapnik/blob/master/deps/agg/include/agg_gamma_functions.h" - }, - "stroke-dasharray": { - "css": "line-dasharray", - "type": "numbers", - "doc": "A pair of length values [a,b], where (a) is the dash length and (b) is the gap length respectively. More than two values are supported for more complex patterns.", - "default-value": "none", - "default-meaning": "solid line" - }, - "stroke-dashoffset": { - "css": "line-dash-offset", - "type": "numbers", - "doc": "valid parameter but not currently used in renderers (only exists for experimental svg support in Mapnik which is not yet enabled)", - "default-value": "none", - "default-meaning": "solid line" - }, - "stroke-miterlimit": { - "css": "line-miterlimit", - "type": "float", - "doc": "The limit on the ratio of the miter length to the stroke-width. Used to automatically convert miter joins to bevel joins for sharp angles to avoid the miter extending beyond the thickness of the stroking path. Normally will not need to be set, but a larger value can sometimes help avoid jaggy artifacts.", - "default-value": 4.0, - "default-meaning": "Will auto-convert miters to bevel line joins when theta is less than 29 degrees as per the SVG spec: 'miterLength / stroke-width = 1 / sin ( theta / 2 )'" - }, - "clip": { - "css": "line-clip", - "type": "boolean", - "default-value": true, - "default-meaning": "geometry will be clipped to map bounds before rendering", - "doc": "geometries are clipped to map bounds by default for best rendering performance. In some cases users may wish to disable this to avoid rendering artifacts." - }, - "smooth": { - "css": "line-smooth", - "type": "float", - "default-value": 0, - "default-meaning": "no smoothing", - "range": "0-1", - "doc": "Smooths out geometry angles. 0 is no smoothing, 1 is fully smoothed. Values greater than 1 will produce wild, looping geometries." - }, - "offset": { - "css": "line-offset", - "type": "float", - "default-value": 0, - "default-meaning": "no offset", - "doc": "Offsets a line a number of pixels parallel to its actual path. Postive values move the line left, negative values move it right (relative to the directionality of the line)." - }, - "rasterizer": { - "css": "line-rasterizer", - "type": [ - "full", - "fast" - ], - "default-value": "full", - "doc": "Exposes an alternate AGG rendering method that sacrifices some accuracy for speed." - }, - "geometry-transform": { - "css": "line-geometry-transform", - "type": "functions", - "default-value": "none", - "default-meaning": "geometry will not be transformed", - "doc": "Allows transformation functions to be applied to the geometry.", - "functions": [ - ["matrix", 6], - ["translate", 2], - ["scale", 2], - ["rotate", 3], - ["skewX", 1], - ["skewY", 1] - ] - }, - "comp-op": { - "css": "line-comp-op", - "default-value": "src-over", - "default-meaning": "add the current symbolizer on top of other symbolizer", - "doc": "Composite operation. This defines how this symbolizer should behave relative to symbolizers atop or below it.", - "type": ["clear", - "src", - "dst", - "src-over", - "dst-over", - "src-in", - "dst-in", - "src-out", - "dst-out", - "src-atop", - "dst-atop", - "xor", - "plus", - "minus", - "multiply", - "screen", - "overlay", - "darken", - "lighten", - "color-dodge", - "color-burn", - "hard-light", - "soft-light", - "difference", - "exclusion", - "contrast", - "invert", - "invert-rgb", - "grain-merge", - "grain-extract", - "hue", - "saturation", - "color", - "value" - ] - } - }, - "markers": { - "file": { - "css": "marker-file", - "doc": "An SVG file that this marker shows at each placement. If no file is given, the marker will show an ellipse.", - "default-value": "", - "default-meaning": "An ellipse or circle, if width equals height", - "type": "uri" - }, - "opacity": { - "css": "marker-opacity", - "doc": "The overall opacity of the marker, if set, overrides both the opacity of both the fill and stroke", - "default-value": 1, - "default-meaning": "The stroke-opacity and fill-opacity will be used", - "type": "float" - }, - "fill-opacity": { - "css": "marker-fill-opacity", - "doc": "The fill opacity of the marker", - "default-value": 1, - "default-meaning": "opaque", - "type": "float" - }, - "stroke": { - "css": "marker-line-color", - "doc": "The color of the stroke around a marker shape.", - "default-value": "black", - "type": "color" - }, - "stroke-width": { - "css": "marker-line-width", - "doc": "The width of the stroke around a marker shape, in pixels. This is positioned on the boundary, so high values can cover the area itself.", - "type": "float" - }, - "stroke-opacity": { - "css": "marker-line-opacity", - "default-value": 1, - "default-meaning": "opaque", - "doc": "The opacity of a line", - "type": "float" - }, - "placement": { - "css": "marker-placement", - "type": [ - "point", - "line", - "interior" - ], - "default-value": "point", - "default-meaning": "Place markers at the center point (centroid) of the geometry", - "doc": "Attempt to place markers on a point, in the center of a polygon, or if markers-placement:line, then multiple times along a line. 'interior' placement can be used to ensure that points placed on polygons are forced to be inside the polygon interior" - }, - "multi-policy": { - "css": "marker-multi-policy", - "type": [ - "each", - "whole", - "largest" - ], - "default-value": "each", - "default-meaning": "If a feature contains multiple geometries and the placement type is either point or interior then a marker will be rendered for each", - "doc": "A special setting to allow the user to control rendering behavior for 'multi-geometries' (when a feature contains multiple geometries). This setting does not apply to markers placed along lines. The 'each' policy is default and means all geometries will get a marker. The 'whole' policy means that the aggregate centroid between all geometries will be used. The 'largest' policy means that only the largest (by bounding box areas) feature will get a rendered marker (this is how text labeling behaves by default)." - }, - "marker-type": { - "css": "marker-type", - "type": [ - "arrow", - "ellipse", - "rectangle" - ], - "default-value": "ellipse", - "doc": "The default marker-type. If a SVG file is not given as the marker-file parameter, the renderer provides either an arrow or an ellipse (a circle if height is equal to width)" - }, - "width": { - "css": "marker-width", - "default-value": 10, - "doc": "The width of the marker, if using one of the default types.", - "type": "expression" - }, - "height": { - "css": "marker-height", - "default-value": 10, - "doc": "The height of the marker, if using one of the default types.", - "type": "expression" - }, - "fill": { - "css": "marker-fill", - "default-value": "blue", - "doc": "The color of the area of the marker.", - "type": "color" - }, - "allow-overlap": { - "css": "marker-allow-overlap", - "type": "boolean", - "default-value": false, - "doc": "Control whether overlapping markers are shown or hidden.", - "default-meaning": "Do not allow makers to overlap with each other - overlapping markers will not be shown." - }, - "ignore-placement": { - "css": "marker-ignore-placement", - "type": "boolean", - "default-value": false, - "default-meaning": "do not store the bbox of this geometry in the collision detector cache", - "doc": "value to control whether the placement of the feature will prevent the placement of other features" - }, - "spacing": { - "css": "marker-spacing", - "doc": "Space between repeated labels", - "default-value": 100, - "type": "float" - }, - "max-error": { - "css": "marker-max-error", - "type": "float", - "default-value": 0.2, - "doc": "The maximum difference between actual marker placement and the marker-spacing parameter. Setting a high value can allow the renderer to try to resolve placement conflicts with other symbolizers." - }, - "transform": { - "css": "marker-transform", - "type": "functions", - "functions": [ - ["matrix", 6], - ["translate", 2], - ["scale", 2], - ["rotate", 3], - ["skewX", 1], - ["skewY", 1] - ], - "default-value": "", - "default-meaning": "No transformation", - "doc": "SVG transformation definition" - }, - "clip": { - "css": "marker-clip", - "type": "boolean", - "default-value": true, - "default-meaning": "geometry will be clipped to map bounds before rendering", - "doc": "geometries are clipped to map bounds by default for best rendering performance. In some cases users may wish to disable this to avoid rendering artifacts." - }, - "smooth": { - "css": "marker-smooth", - "type": "float", - "default-value": 0, - "default-meaning": "no smoothing", - "range": "0-1", - "doc": "Smooths out geometry angles. 0 is no smoothing, 1 is fully smoothed. Values greater than 1 will produce wild, looping geometries." - }, - "geometry-transform": { - "css": "marker-geometry-transform", - "type": "functions", - "default-value": "none", - "default-meaning": "geometry will not be transformed", - "doc": "Allows transformation functions to be applied to the geometry.", - "functions": [ - ["matrix", 6], - ["translate", 2], - ["scale", 2], - ["rotate", 3], - ["skewX", 1], - ["skewY", 1] - ] - }, - "comp-op": { - "css": "marker-comp-op", - "default-value": "src-over", - "default-meaning": "add the current symbolizer on top of other symbolizer", - "doc": "Composite operation. This defines how this symbolizer should behave relative to symbolizers atop or below it.", - "type": ["clear", - "src", - "dst", - "src-over", - "dst-over", - "src-in", - "dst-in", - "src-out", - "dst-out", - "src-atop", - "dst-atop", - "xor", - "plus", - "minus", - "multiply", - "screen", - "overlay", - "darken", - "lighten", - "color-dodge", - "color-burn", - "hard-light", - "soft-light", - "difference", - "exclusion", - "contrast", - "invert", - "invert-rgb", - "grain-merge", - "grain-extract", - "hue", - "saturation", - "color", - "value" - ] - } - }, - "shield": { - "name": { - "css": "shield-name", - "type": "expression", - "serialization": "content", - "doc": "Value to use for a shield\"s text label. Data columns are specified using brackets like [column_name]" - }, - "file": { - "css": "shield-file", - "required": true, - "type": "uri", - "default-value": "none", - "doc": "Image file to render behind the shield text" - }, - "face-name": { - "css": "shield-face-name", - "type": "string", - "validate": "font", - "doc": "Font name and style to use for the shield text", - "default-value": "", - "required": true - }, - "unlock-image": { - "css": "shield-unlock-image", - "type": "boolean", - "doc": "This parameter should be set to true if you are trying to position text beside rather than on top of the shield image", - "default-value": false, - "default-meaning": "text alignment relative to the shield image uses the center of the image as the anchor for text positioning." - }, - "size": { - "css": "shield-size", - "type": "float", - "doc": "The size of the shield text in pixels" - }, - "fill": { - "css": "shield-fill", - "type": "color", - "doc": "The color of the shield text" - }, - "placement": { - "css": "shield-placement", - "type": [ - "point", - "line", - "vertex", - "interior" - ], - "default-value": "point", - "doc": "How this shield should be placed. Point placement attempts to place it on top of points, line places along lines multiple times per feature, vertex places on the vertexes of polygons, and interior attempts to place inside of polygons." - }, - "avoid-edges": { - "css": "shield-avoid-edges", - "doc": "Tell positioning algorithm to avoid labeling near intersection edges.", - "type": "boolean", - "default-value": false - }, - "allow-overlap": { - "css": "shield-allow-overlap", - "type": "boolean", - "default-value": false, - "doc": "Control whether overlapping shields are shown or hidden.", - "default-meaning": "Do not allow shields to overlap with other map elements already placed." - }, - "minimum-distance": { - "css": "shield-min-distance", - "type": "float", - "default-value": 0, - "doc": "Minimum distance to the next shield symbol, not necessarily the same shield." - }, - "spacing": { - "css": "shield-spacing", - "type": "float", - "default-value": 0, - "doc": "The spacing between repeated occurrences of the same shield on a line" - }, - "minimum-padding": { - "css": "shield-min-padding", - "default-value": 0, - "doc": "Determines the minimum amount of padding that a shield gets relative to other shields", - "type": "float" - }, - "wrap-width": { - "css": "shield-wrap-width", - "type": "unsigned", - "default-value": 0, - "doc": "Length of a chunk of text in characters before wrapping text" - }, - "wrap-before": { - "css": "shield-wrap-before", - "type": "boolean", - "default-value": false, - "doc": "Wrap text before wrap-width is reached. If false, wrapped lines will be a bit longer than wrap-width." - }, - "wrap-character": { - "css": "shield-wrap-character", - "type": "string", - "default-value": " ", - "doc": "Use this character instead of a space to wrap long names." - }, - "halo-fill": { - "css": "shield-halo-fill", - "type": "color", - "default-value": "#FFFFFF", - "default-meaning": "white", - "doc": "Specifies the color of the halo around the text." - }, - "halo-radius": { - "css": "shield-halo-radius", - "doc": "Specify the radius of the halo in pixels", - "default-value": 0, - "default-meaning": "no halo", - "type": "float" - }, - "character-spacing": { - "css": "shield-character-spacing", - "type": "unsigned", - "default-value": 0, - "doc": "Horizontal spacing between characters (in pixels). Currently works for point placement only, not line placement." - }, - "line-spacing": { - "css": "shield-line-spacing", - "doc": "Vertical spacing between lines of multiline labels (in pixels)", - "type": "unsigned" - }, - "dx": { - "css": "shield-text-dx", - "type": "float", - "doc": "Displace text within shield by fixed amount, in pixels, +/- along the X axis. A positive value will shift the text right", - "default-value": 0 - }, - "dy": { - "css": "shield-text-dy", - "type": "float", - "doc": "Displace text within shield by fixed amount, in pixels, +/- along the Y axis. A positive value will shift the text down", - "default-value": 0 - }, - "shield-dx": { - "css": "shield-dx", - "type": "float", - "doc": "Displace shield by fixed amount, in pixels, +/- along the X axis. A positive value will shift the text right", - "default-value": 0 - }, - "shield-dy": { - "css": "shield-dy", - "type": "float", - "doc": "Displace shield by fixed amount, in pixels, +/- along the Y axis. A positive value will shift the text down", - "default-value": 0 - }, - "opacity": { - "css": "shield-opacity", - "type": "float", - "doc": "(Default 1.0) - opacity of the image used for the shield", - "default-value": 1 - }, - "text-opacity": { - "css": "shield-text-opacity", - "type": "float", - "doc": "(Default 1.0) - opacity of the text placed on top of the shield", - "default-value": 1 - }, - "horizontal-alignment": { - "css": "shield-horizontal-alignment", - "type": [ - "left", - "middle", - "right", - "auto" - ], - "doc": "The shield's horizontal alignment from its centerpoint", - "default-value": "auto" - }, - "vertical-alignment": { - "css": "shield-vertical-alignment", - "type": [ - "top", - "middle", - "bottom", - "auto" - ], - "doc": "The shield's vertical alignment from its centerpoint", - "default-value": "middle" - }, - "text-transform": { - "css": "shield-text-transform", - "type": [ - "none", - "uppercase", - "lowercase", - "capitalize" - ], - "doc": "Transform the case of the characters", - "default-value": "none" - }, - "justify-alignment": { - "css": "shield-justify-alignment", - "type": [ - "left", - "center", - "right", - "auto" - ], - "doc": "Define how text in a shield's label is justified", - "default-value": "auto" - }, - "clip": { - "css": "shield-clip", - "type": "boolean", - "default-value": true, - "default-meaning": "geometry will be clipped to map bounds before rendering", - "doc": "geometries are clipped to map bounds by default for best rendering performance. In some cases users may wish to disable this to avoid rendering artifacts." - }, - "comp-op": { - "css": "shield-comp-op", - "default-value": "src-over", - "default-meaning": "add the current symbolizer on top of other symbolizer", - "doc": "Composite operation. This defines how this symbolizer should behave relative to symbolizers atop or below it.", - "type": ["clear", - "src", - "dst", - "src-over", - "dst-over", - "src-in", - "dst-in", - "src-out", - "dst-out", - "src-atop", - "dst-atop", - "xor", - "plus", - "minus", - "multiply", - "screen", - "overlay", - "darken", - "lighten", - "color-dodge", - "color-burn", - "hard-light", - "soft-light", - "difference", - "exclusion", - "contrast", - "invert", - "invert-rgb", - "grain-merge", - "grain-extract", - "hue", - "saturation", - "color", - "value" - ] - } - }, - "line-pattern": { - "file": { - "css": "line-pattern-file", - "type": "uri", - "default-value": "none", - "required": true, - "doc": "An image file to be repeated and warped along a line" - }, - "clip": { - "css": "line-pattern-clip", - "type": "boolean", - "default-value": true, - "default-meaning": "geometry will be clipped to map bounds before rendering", - "doc": "geometries are clipped to map bounds by default for best rendering performance. In some cases users may wish to disable this to avoid rendering artifacts." - }, - "smooth": { - "css": "line-pattern-smooth", - "type": "float", - "default-value": 0, - "default-meaning": "no smoothing", - "range": "0-1", - "doc": "Smooths out geometry angles. 0 is no smoothing, 1 is fully smoothed. Values greater than 1 will produce wild, looping geometries." - }, - "geometry-transform": { - "css": "line-pattern-geometry-transform", - "type": "functions", - "default-value": "none", - "default-meaning": "geometry will not be transformed", - "doc": "Allows transformation functions to be applied to the geometry.", - "functions": [ - ["matrix", 6], - ["translate", 2], - ["scale", 2], - ["rotate", 3], - ["skewX", 1], - ["skewY", 1] - ] - }, - "comp-op": { - "css": "line-pattern-comp-op", - "default-value": "src-over", - "default-meaning": "add the current symbolizer on top of other symbolizer", - "doc": "Composite operation. This defines how this symbolizer should behave relative to symbolizers atop or below it.", - "type": ["clear", - "src", - "dst", - "src-over", - "dst-over", - "src-in", - "dst-in", - "src-out", - "dst-out", - "src-atop", - "dst-atop", - "xor", - "plus", - "minus", - "multiply", - "screen", - "overlay", - "darken", - "lighten", - "color-dodge", - "color-burn", - "hard-light", - "soft-light", - "difference", - "exclusion", - "contrast", - "invert", - "invert-rgb", - "grain-merge", - "grain-extract", - "hue", - "saturation", - "color", - "value" - ] - } - }, - "polygon-pattern": { - "file": { - "css": "polygon-pattern-file", - "type": "uri", - "default-value": "none", - "required": true, - "doc": "Image to use as a repeated pattern fill within a polygon" - }, - "alignment": { - "css": "polygon-pattern-alignment", - "type": [ - "local", - "global" - ], - "default-value": "local", - "doc": "Specify whether to align pattern fills to the layer or to the map." - }, - "gamma": { - "css": "polygon-pattern-gamma", - "type": "float", - "default-value": 1, - "default-meaning": "fully antialiased", - "range": "0-1", - "doc": "Level of antialiasing of polygon pattern edges" - }, - "opacity": { - "css": "polygon-pattern-opacity", - "type": "float", - "doc": "(Default 1.0) - Apply an opacity level to the image used for the pattern", - "default-value": 1, - "default-meaning": "The image is rendered without modifications" - }, - "clip": { - "css": "polygon-pattern-clip", - "type": "boolean", - "default-value": true, - "default-meaning": "geometry will be clipped to map bounds before rendering", - "doc": "geometries are clipped to map bounds by default for best rendering performance. In some cases users may wish to disable this to avoid rendering artifacts." - }, - "smooth": { - "css": "polygon-pattern-smooth", - "type": "float", - "default-value": 0, - "default-meaning": "no smoothing", - "range": "0-1", - "doc": "Smooths out geometry angles. 0 is no smoothing, 1 is fully smoothed. Values greater than 1 will produce wild, looping geometries." - }, - "geometry-transform": { - "css": "polygon-pattern-geometry-transform", - "type": "functions", - "default-value": "none", - "default-meaning": "geometry will not be transformed", - "doc": "Allows transformation functions to be applied to the geometry.", - "functions": [ - ["matrix", 6], - ["translate", 2], - ["scale", 2], - ["rotate", 3], - ["skewX", 1], - ["skewY", 1] - ] - }, - "comp-op": { - "css": "polygon-pattern-comp-op", - "default-value": "src-over", - "default-meaning": "add the current symbolizer on top of other symbolizer", - "doc": "Composite operation. This defines how this symbolizer should behave relative to symbolizers atop or below it.", - "type": ["clear", - "src", - "dst", - "src-over", - "dst-over", - "src-in", - "dst-in", - "src-out", - "dst-out", - "src-atop", - "dst-atop", - "xor", - "plus", - "minus", - "multiply", - "screen", - "overlay", - "darken", - "lighten", - "color-dodge", - "color-burn", - "hard-light", - "soft-light", - "difference", - "exclusion", - "contrast", - "invert", - "invert-rgb", - "grain-merge", - "grain-extract", - "hue", - "saturation", - "color", - "value" - ] - } - }, - "raster": { - "opacity": { - "css": "raster-opacity", - "default-value": 1, - "default-meaning": "opaque", - "type": "float", - "doc": "The opacity of the raster symbolizer on top of other symbolizers." - }, - "filter-factor": { - "css": "raster-filter-factor", - "default-value": -1, - "default-meaning": "Allow the datasource to choose appropriate downscaling.", - "type": "float", - "doc": "This is used by the Raster or Gdal datasources to pre-downscale images using overviews. Higher numbers can sometimes cause much better scaled image output, at the cost of speed." - }, - "scaling": { - "css": "raster-scaling", - "type": [ - "near", - "fast", - "bilinear", - "bilinear8", - "bicubic", - "spline16", - "spline36", - "hanning", - "hamming", - "hermite", - "kaiser", - "quadric", - "catrom", - "gaussian", - "bessel", - "mitchell", - "sinc", - "lanczos", - "blackman" - ], - "default-value": "near", - "doc": "The scaling algorithm used to making different resolution versions of this raster layer. Bilinear is a good compromise between speed and accuracy, while lanczos gives the highest quality." - }, - "mesh-size": { - "css": "raster-mesh-size", - "default-value": 16, - "default-meaning": "Reprojection mesh will be 1/16 of the resolution of the source image", - "type": "unsigned", - "doc": "A reduced resolution mesh is used for raster reprojection, and the total image size is divided by the mesh-size to determine the quality of that mesh. Values for mesh-size larger than the default will result in faster reprojection but might lead to distortion." - }, - "comp-op": { - "css": "raster-comp-op", - "default-value": "src-over", - "default-meaning": "add the current symbolizer on top of other symbolizer", - "doc": "Composite operation. This defines how this symbolizer should behave relative to symbolizers atop or below it.", - "type": ["clear", - "src", - "dst", - "src-over", - "dst-over", - "src-in", - "dst-in", - "src-out", - "dst-out", - "src-atop", - "dst-atop", - "xor", - "plus", - "minus", - "multiply", - "screen", - "overlay", - "darken", - "lighten", - "color-dodge", - "color-burn", - "hard-light", - "soft-light", - "difference", - "exclusion", - "contrast", - "invert", - "invert-rgb", - "grain-merge", - "grain-extract", - "hue", - "saturation", - "color", - "value" - ] - } - }, - "point": { - "file": { - "css": "point-file", - "type": "uri", - "required": false, - "default-value": "none", - "doc": "Image file to represent a point" - }, - "allow-overlap": { - "css": "point-allow-overlap", - "type": "boolean", - "default-value": false, - "doc": "Control whether overlapping points are shown or hidden.", - "default-meaning": "Do not allow points to overlap with each other - overlapping markers will not be shown." - }, - "ignore-placement": { - "css": "point-ignore-placement", - "type": "boolean", - "default-value": false, - "default-meaning": "do not store the bbox of this geometry in the collision detector cache", - "doc": "value to control whether the placement of the feature will prevent the placement of other features" - }, - "opacity": { - "css": "point-opacity", - "type": "float", - "default-value": 1.0, - "default-meaning": "Fully opaque", - "doc": "A value from 0 to 1 to control the opacity of the point" - }, - "placement": { - "css": "point-placement", - "type": [ - "centroid", - "interior" - ], - "doc": "How this point should be placed. Centroid calculates the geometric center of a polygon, which can be outside of it, while interior always places inside of a polygon.", - "default-value": "centroid" - }, - "transform": { - "css": "point-transform", - "type": "functions", - "functions": [ - ["matrix", 6], - ["translate", 2], - ["scale", 2], - ["rotate", 3], - ["skewX", 1], - ["skewY", 1] - ], - "default-value": "", - "default-meaning": "No transformation", - "doc": "SVG transformation definition" - }, - "comp-op": { - "css": "point-comp-op", - "default-value": "src-over", - "default-meaning": "add the current symbolizer on top of other symbolizer", - "doc": "Composite operation. This defines how this symbolizer should behave relative to symbolizers atop or below it.", - "type": ["clear", - "src", - "dst", - "src-over", - "dst-over", - "src-in", - "dst-in", - "src-out", - "dst-out", - "src-atop", - "dst-atop", - "xor", - "plus", - "minus", - "multiply", - "screen", - "overlay", - "darken", - "lighten", - "color-dodge", - "color-burn", - "hard-light", - "soft-light", - "difference", - "exclusion", - "contrast", - "invert", - "invert-rgb", - "grain-merge", - "grain-extract", - "hue", - "saturation", - "color", - "value" - ] - } - }, - "text": { - "name": { - "css": "text-name", - "type": "expression", - "required": true, - "default-value": "", - "serialization": "content", - "doc": "Value to use for a text label. Data columns are specified using brackets like [column_name]" - }, - "face-name": { - "css": "text-face-name", - "type": "string", - "validate": "font", - "doc": "Font name and style to render a label in", - "required": true - }, - "size": { - "css": "text-size", - "type": "float", - "default-value": 10, - "doc": "Text size in pixels" - }, - "text-ratio": { - "css": "text-ratio", - "doc": "Define the amount of text (of the total) present on successive lines when wrapping occurs", - "default-value": 0, - "type": "unsigned" - }, - "wrap-width": { - "css": "text-wrap-width", - "doc": "Length of a chunk of text in characters before wrapping text", - "default-value": 0, - "type": "unsigned" - }, - "wrap-before": { - "css": "text-wrap-before", - "type": "boolean", - "default-value": false, - "doc": "Wrap text before wrap-width is reached. If false, wrapped lines will be a bit longer than wrap-width." - }, - "wrap-character": { - "css": "text-wrap-character", - "type": "string", - "default-value": " ", - "doc": "Use this character instead of a space to wrap long text." - }, - "spacing": { - "css": "text-spacing", - "type": "unsigned", - "doc": "Distance between repeated text labels on a line (aka. label-spacing)" - }, - "character-spacing": { - "css": "text-character-spacing", - "type": "float", - "default-value": 0, - "doc": "Horizontal spacing adjustment between characters in pixels" - }, - "line-spacing": { - "css": "text-line-spacing", - "default-value": 0, - "type": "unsigned", - "doc": "Vertical spacing adjustment between lines in pixels" - }, - "label-position-tolerance": { - "css": "text-label-position-tolerance", - "default-value": 0, - "type": "unsigned", - "doc": "Allows the label to be displaced from its ideal position by a number of pixels (only works with placement:line)" - }, - "max-char-angle-delta": { - "css": "text-max-char-angle-delta", - "type": "float", - "default-value": "22.5", - "doc": "The maximum angle change, in degrees, allowed between adjacent characters in a label. This value internally is converted to radians to the default is 22.5*math.pi/180.0. The higher the value the fewer labels will be placed around around sharp corners." - }, - "fill": { - "css": "text-fill", - "doc": "Specifies the color for the text", - "default-value": "#000000", - "type": "color" - }, - "opacity": { - "css": "text-opacity", - "doc": "A number from 0 to 1 specifying the opacity for the text", - "default-value": 1.0, - "default-meaning": "Fully opaque", - "type": "float" - }, - "halo-fill": { - "css": "text-halo-fill", - "type": "color", - "default-value": "#FFFFFF", - "default-meaning": "white", - "doc": "Specifies the color of the halo around the text." - }, - "halo-radius": { - "css": "text-halo-radius", - "doc": "Specify the radius of the halo in pixels", - "default-value": 0, - "default-meaning": "no halo", - "type": "float" - }, - "dx": { - "css": "text-dx", - "type": "float", - "doc": "Displace text by fixed amount, in pixels, +/- along the X axis. A positive value will shift the text right", - "default-value": 0 - }, - "dy": { - "css": "text-dy", - "type": "float", - "doc": "Displace text by fixed amount, in pixels, +/- along the Y axis. A positive value will shift the text down", - "default-value": 0 - }, - "vertical-alignment": { - "css": "text-vertical-alignment", - "type": [ - "top", - "middle", - "bottom", - "auto" - ], - "doc": "Position of label relative to point position.", - "default-value": "auto", - "default-meaning": "Default affected by value of dy; \"bottom\" for dy>0, \"top\" for dy<0." - }, - "avoid-edges": { - "css": "text-avoid-edges", - "doc": "Tell positioning algorithm to avoid labeling near intersection edges.", - "default-value": false, - "type": "boolean" - }, - "minimum-distance": { - "css": "text-min-distance", - "doc": "Minimum permitted distance to the next text symbolizer.", - "type": "float" - }, - "minimum-padding": { - "css": "text-min-padding", - "doc": "Determines the minimum amount of padding that a text symbolizer gets relative to other text", - "type": "float" - }, - "minimum-path-length": { - "css": "text-min-path-length", - "type": "float", - "default-value": 0, - "default-meaning": "place labels on all paths", - "doc": "Place labels only on paths longer than this value." - }, - "allow-overlap": { - "css": "text-allow-overlap", - "type": "boolean", - "default-value": false, - "doc": "Control whether overlapping text is shown or hidden.", - "default-meaning": "Do not allow text to overlap with other text - overlapping markers will not be shown." - }, - "orientation": { - "css": "text-orientation", - "type": "expression", - "doc": "Rotate the text." - }, - "placement": { - "css": "text-placement", - "type": [ - "point", - "line", - "vertex", - "interior" - ], - "default-value": "point", - "doc": "Control the style of placement of a point versus the geometry it is attached to." - }, - "placement-type": { - "css": "text-placement-type", - "doc": "Re-position and/or re-size text to avoid overlaps. \"simple\" for basic algorithm (using text-placements string,) \"dummy\" to turn this feature off.", - "type": [ - "dummy", - "simple" - ], - "default-value": "dummy" - }, - "placements": { - "css": "text-placements", - "type": "string", - "default-value": "", - "doc": "If \"placement-type\" is set to \"simple\", use this \"POSITIONS,[SIZES]\" string. An example is `text-placements: \"E,NE,SE,W,NW,SW\";` " - }, - "text-transform": { - "css": "text-transform", - "type": [ - "none", - "uppercase", - "lowercase", - "capitalize" - ], - "doc": "Transform the case of the characters", - "default-value": "none" - }, - "horizontal-alignment": { - "css": "text-horizontal-alignment", - "type": [ - "left", - "middle", - "right", - "auto" - ], - "doc": "The text's horizontal alignment from its centerpoint", - "default-value": "auto" - }, - "justify-alignment": { - "css": "text-align", - "type": [ - "left", - "right", - "center", - "auto" - ], - "doc": "Define how text is justified", - "default-value": "auto", - "default-meaning": "Auto alignment means that text will be centered by default except when using the `placement-type` parameter - in that case either right or left justification will be used automatically depending on where the text could be fit given the `text-placements` directives" - }, - "clip": { - "css": "text-clip", - "type": "boolean", - "default-value": true, - "default-meaning": "geometry will be clipped to map bounds before rendering", - "doc": "geometries are clipped to map bounds by default for best rendering performance. In some cases users may wish to disable this to avoid rendering artifacts." - }, - "comp-op": { - "css": "text-comp-op", - "default-value": "src-over", - "default-meaning": "add the current symbolizer on top of other symbolizer", - "doc": "Composite operation. This defines how this symbolizer should behave relative to symbolizers atop or below it.", - "type": ["clear", - "src", - "dst", - "src-over", - "dst-over", - "src-in", - "dst-in", - "src-out", - "dst-out", - "src-atop", - "dst-atop", - "xor", - "plus", - "minus", - "multiply", - "screen", - "overlay", - "darken", - "lighten", - "color-dodge", - "color-burn", - "hard-light", - "soft-light", - "difference", - "exclusion", - "contrast", - "invert", - "invert-rgb", - "grain-merge", - "grain-extract", - "hue", - "saturation", - "color", - "value" - ] - } - }, - "building": { - "fill": { - "css": "building-fill", - "default-value": "#FFFFFF", - "doc": "The color of the buildings walls.", - "type": "color" - }, - "fill-opacity": { - "css": "building-fill-opacity", - "type": "float", - "doc": "The opacity of the building as a whole, including all walls.", - "default-value": 1 - }, - "height": { - "css": "building-height", - "doc": "The height of the building in pixels.", - "type": "expression", - "default-value": "0" - } - }, - "torque": { - "-torque-frame-count": { - "css": "-torque-frame-count", - "default-value": "128", - "type":"float", - "default-meaning": "the data is broken into 128 time frames", - "doc": "Number of animation steps/frames used in the animation. If the data contains a fewere number of total frames, the lesser value will be used." - }, - "-torque-resolution": { - "css": "-torque-resolution", - "default-value": "2", - "type":"float", - "default-meaning": "", - "doc": "Spatial resolution in pixels. A resolution of 1 means no spatial aggregation of the data. Any other resolution of N results in spatial aggregation into cells of NxN pixels. The value N must be power of 2" - }, - "-torque-animation-duration": { - "css": "-torque-animation-duration", - "default-value": "30", - "type":"float", - "default-meaning": "the animation lasts 30 seconds", - "doc": "Animation duration in seconds" - }, - "-torque-aggregation-function": { - "css": "-torque-aggregation-function", - "default-value": "count(cartodb_id)", - "type": "string", - "default-meaning": "the value for each cell is the count of points in that cell", - "doc": "A function used to calculate a value from the aggregate data for each cell. See -torque-resolution" - }, - "-torque-time-attribute": { - "css": "-torque-time-attribute", - "default-value": "time", - "type": "string", - "default-meaning": "the data column in your table that is of a time based type", - "doc": "The table column that contains the time information used create the animation" - }, - "-torque-data-aggregation": { - "css": "-torque-data-aggregation", - "default-value": "linear", - "type": [ - "linear", - "cumulative" - ], - "default-meaning": "previous values are discarded", - "doc": "A linear animation will discard previous values while a cumulative animation will accumulate them until it restarts" - } - } - }, - "colors": { - "aliceblue": [240, 248, 255], - "antiquewhite": [250, 235, 215], - "aqua": [0, 255, 255], - "aquamarine": [127, 255, 212], - "azure": [240, 255, 255], - "beige": [245, 245, 220], - "bisque": [255, 228, 196], - "black": [0, 0, 0], - "blanchedalmond": [255,235,205], - "blue": [0, 0, 255], - "blueviolet": [138, 43, 226], - "brown": [165, 42, 42], - "burlywood": [222, 184, 135], - "cadetblue": [95, 158, 160], - "chartreuse": [127, 255, 0], - "chocolate": [210, 105, 30], - "coral": [255, 127, 80], - "cornflowerblue": [100, 149, 237], - "cornsilk": [255, 248, 220], - "crimson": [220, 20, 60], - "cyan": [0, 255, 255], - "darkblue": [0, 0, 139], - "darkcyan": [0, 139, 139], - "darkgoldenrod": [184, 134, 11], - "darkgray": [169, 169, 169], - "darkgreen": [0, 100, 0], - "darkgrey": [169, 169, 169], - "darkkhaki": [189, 183, 107], - "darkmagenta": [139, 0, 139], - "darkolivegreen": [85, 107, 47], - "darkorange": [255, 140, 0], - "darkorchid": [153, 50, 204], - "darkred": [139, 0, 0], - "darksalmon": [233, 150, 122], - "darkseagreen": [143, 188, 143], - "darkslateblue": [72, 61, 139], - "darkslategrey": [47, 79, 79], - "darkturquoise": [0, 206, 209], - "darkviolet": [148, 0, 211], - "deeppink": [255, 20, 147], - "deepskyblue": [0, 191, 255], - "dimgray": [105, 105, 105], - "dimgrey": [105, 105, 105], - "dodgerblue": [30, 144, 255], - "firebrick": [178, 34, 34], - "floralwhite": [255, 250, 240], - "forestgreen": [34, 139, 34], - "fuchsia": [255, 0, 255], - "gainsboro": [220, 220, 220], - "ghostwhite": [248, 248, 255], - "gold": [255, 215, 0], - "goldenrod": [218, 165, 32], - "gray": [128, 128, 128], - "grey": [128, 128, 128], - "green": [0, 128, 0], - "greenyellow": [173, 255, 47], - "honeydew": [240, 255, 240], - "hotpink": [255, 105, 180], - "indianred": [205, 92, 92], - "indigo": [75, 0, 130], - "ivory": [255, 255, 240], - "khaki": [240, 230, 140], - "lavender": [230, 230, 250], - "lavenderblush": [255, 240, 245], - "lawngreen": [124, 252, 0], - "lemonchiffon": [255, 250, 205], - "lightblue": [173, 216, 230], - "lightcoral": [240, 128, 128], - "lightcyan": [224, 255, 255], - "lightgoldenrodyellow": [250, 250, 210], - "lightgray": [211, 211, 211], - "lightgreen": [144, 238, 144], - "lightgrey": [211, 211, 211], - "lightpink": [255, 182, 193], - "lightsalmon": [255, 160, 122], - "lightseagreen": [32, 178, 170], - "lightskyblue": [135, 206, 250], - "lightslategray": [119, 136, 153], - "lightslategrey": [119, 136, 153], - "lightsteelblue": [176, 196, 222], - "lightyellow": [255, 255, 224], - "lime": [0, 255, 0], - "limegreen": [50, 205, 50], - "linen": [250, 240, 230], - "magenta": [255, 0, 255], - "maroon": [128, 0, 0], - "mediumaquamarine": [102, 205, 170], - "mediumblue": [0, 0, 205], - "mediumorchid": [186, 85, 211], - "mediumpurple": [147, 112, 219], - "mediumseagreen": [60, 179, 113], - "mediumslateblue": [123, 104, 238], - "mediumspringgreen": [0, 250, 154], - "mediumturquoise": [72, 209, 204], - "mediumvioletred": [199, 21, 133], - "midnightblue": [25, 25, 112], - "mintcream": [245, 255, 250], - "mistyrose": [255, 228, 225], - "moccasin": [255, 228, 181], - "navajowhite": [255, 222, 173], - "navy": [0, 0, 128], - "oldlace": [253, 245, 230], - "olive": [128, 128, 0], - "olivedrab": [107, 142, 35], - "orange": [255, 165, 0], - "orangered": [255, 69, 0], - "orchid": [218, 112, 214], - "palegoldenrod": [238, 232, 170], - "palegreen": [152, 251, 152], - "paleturquoise": [175, 238, 238], - "palevioletred": [219, 112, 147], - "papayawhip": [255, 239, 213], - "peachpuff": [255, 218, 185], - "peru": [205, 133, 63], - "pink": [255, 192, 203], - "plum": [221, 160, 221], - "powderblue": [176, 224, 230], - "purple": [128, 0, 128], - "red": [255, 0, 0], - "rosybrown": [188, 143, 143], - "royalblue": [65, 105, 225], - "saddlebrown": [139, 69, 19], - "salmon": [250, 128, 114], - "sandybrown": [244, 164, 96], - "seagreen": [46, 139, 87], - "seashell": [255, 245, 238], - "sienna": [160, 82, 45], - "silver": [192, 192, 192], - "skyblue": [135, 206, 235], - "slateblue": [106, 90, 205], - "slategray": [112, 128, 144], - "slategrey": [112, 128, 144], - "snow": [255, 250, 250], - "springgreen": [0, 255, 127], - "steelblue": [70, 130, 180], - "tan": [210, 180, 140], - "teal": [0, 128, 128], - "thistle": [216, 191, 216], - "tomato": [255, 99, 71], - "turquoise": [64, 224, 208], - "violet": [238, 130, 238], - "wheat": [245, 222, 179], - "white": [255, 255, 255], - "whitesmoke": [245, 245, 245], - "yellow": [255, 255, 0], - "yellowgreen": [154, 205, 50], - "transparent": [0, 0, 0, 0] - }, - "filter": { - "value": [ - "true", - "false", - "null", - "point", - "linestring", - "polygon", - "collection" - ] - } -} - -window.carto['mapnik-reference'] = { - version: { - latest: _mapnik_reference_latest, - '2.1.1': _mapnik_reference_latest - } -} -// -// Stub out `require` in the browser -// -window.carto = window.carto || {}; -window.carto.underscore = window._; - -function require(arg) { - var mod = window.carto[arg]; - if(!mod) { - mod = window.carto[arg.split('/')[1]]; - } - if(!mod) { - mod = window.carto[arg] - } - if(!mod) { - mod = window[arg.split('/')[1]]; - } - // try global scope - if(!mod) { - mod = window[arg] - } - return mod; -} - -if (typeof(exports) !== 'undefined') { - carto = exports; - tree = require('./tree'); - _ = require('underscore'); -} else { - if (typeof(window.carto) === 'undefined') { window.carto = {}; } - carto = window.carto; - tree = window.carto.tree = {}; - _ = window._; -} - -// carto.js - parser -// -// A relatively straight-forward predictive parser. -// There is no tokenization/lexing stage, the input is parsed -// in one sweep. -// -// To make the parser fast enough to run in the browser, several -// optimization had to be made: -// -// - Matching and slicing on a huge input is often cause of slowdowns. -// The solution is to chunkify the input into smaller strings. -// The chunks are stored in the `chunks` var, -// `j` holds the current chunk index, and `current` holds -// the index of the current chunk in relation to `input`. -// This gives us an almost 4x speed-up. -// -// - In many cases, we don't need to match individual tokens; -// for example, if a value doesn't hold any variables, operations -// or dynamic references, the parser can effectively 'skip' it, -// treating it as a literal. -// An example would be '1px solid #000' - which evaluates to itself, -// we don't need to know what the individual components are. -// The drawback, of course is that you don't get the benefits of -// syntax-checking on the CSS. This gives us a 50% speed-up in the parser, -// and a smaller speed-up in the code-gen. -// -// -// Token matching is done with the `$` function, which either takes -// a terminal string or regexp, or a non-terminal function to call. -// It also takes care of moving all the indices forwards. - -carto.Parser = function Parser(env) { - var input, // LeSS input string - i, // current index in `input` - j, // current chunk - temp, // temporarily holds a chunk's state, for backtracking - memo, // temporarily holds `i`, when backtracking - furthest, // furthest index the parser has gone to - chunks, // chunkified input - current, // index of current chunk, in `input` - parser; - - var that = this; - - // This function is called after all files - // have been imported through `@import`. - var finish = function() {}; - - var imports = this.imports = { - paths: env && env.paths || [], // Search paths, when importing - queue: [], // Files which haven't been imported yet - files: {}, // Holds the imported parse trees - mime: env && env.mime, // MIME type of .carto files - push: function(path, callback) { - var that = this; - this.queue.push(path); - - // - // Import a file asynchronously - // - carto.Parser.importer(path, this.paths, function(root) { - that.queue.splice(that.queue.indexOf(path), 1); // Remove the path from the queue - that.files[path] = root; // Store the root - - callback(root); - - if (that.queue.length === 0) { finish(); } // Call `finish` if we're done importing - }, env); - } - }; - - function save() { - temp = chunks[j]; - memo = i; - current = i; - } - function restore() { - chunks[j] = temp; - i = memo; - current = i; - } - - function sync() { - if (i > current) { - chunks[j] = chunks[j].slice(i - current); - current = i; - } - } - // - // Parse from a token, regexp or string, and move forward if match - // - function $(tok) { - var match, args, length, c, index, endIndex, k; - - // - // Non-terminal - // - if (tok instanceof Function) { - return tok.call(parser.parsers); - // - // Terminal - // - // Either match a single character in the input, - // or match a regexp in the current chunk (chunk[j]). - // - } else if (typeof(tok) === 'string') { - match = input.charAt(i) === tok ? tok : null; - length = 1; - sync(); - } else { - sync(); - - match = tok.exec(chunks[j]); - if (match) { - length = match[0].length; - } else { - return null; - } - } - - // The match is confirmed, add the match length to `i`, - // and consume any extra white-space characters (' ' || '\n') - // which come after that. The reason for this is that LeSS's - // grammar is mostly white-space insensitive. - if (match) { - var mem = i += length; - endIndex = i + chunks[j].length - length; - - while (i < endIndex) { - c = input.charCodeAt(i); - if (! (c === 32 || c === 10 || c === 9)) { break; } - i++; - } - chunks[j] = chunks[j].slice(length + (i - mem)); - current = i; - - if (chunks[j].length === 0 && j < chunks.length - 1) { j++; } - - if (typeof(match) === 'string') { - return match; - } else { - return match.length === 1 ? match[0] : match; - } - } - } - - // Same as $(), but don't change the state of the parser, - // just return the match. - function peek(tok) { - if (typeof(tok) === 'string') { - return input.charAt(i) === tok; - } else { - if (tok.test(chunks[j])) { - return true; - } else { - return false; - } - } - } - - function extractErrorLine(style, errorIndex) { - return (style.slice(0, errorIndex).match(/\n/g) || '').length + 1; - } - - - // Make an error object from a passed set of properties. - // Accepted properties: - // - `message`: Text of the error message. - // - `filename`: Filename where the error occurred. - // - `index`: Char. index where the error occurred. - function makeError(err) { - var einput; - - _(err).defaults({ - index: furthest, - filename: env.filename, - message: 'Parse error.', - line: 0, - column: -1 - }); - - if (err.filename && that.env.inputs && that.env.inputs[err.filename]) { - einput = that.env.inputs[err.filename]; - } else { - einput = input; - } - - err.line = extractErrorLine(einput, err.index); - for (var n = err.index; n >= 0 && einput.charAt(n) !== '\n'; n--) { - err.column++; - } - - return new Error(_('<%=filename%>:<%=line%>:<%=column%> <%=message%>').template(err)); - } - - this.env = env = env || {}; - this.env.filename = this.env.filename || null; - this.env.inputs = this.env.inputs || {}; - - // The Parser - return parser = { - - imports: imports, - extractErrorLine: extractErrorLine, - // - // Parse an input string into an abstract syntax tree. - // Throws an error on parse errors. - parse: function(str) { - var root, start, end, zone, line, lines, buff = [], c, error = null; - - i = j = current = furthest = 0; - chunks = []; - input = str.replace(/\r\n/g, '\n'); - if (env.filename) { - that.env.inputs[env.filename] = input; - } - - var early_exit = false; - // Split the input into chunks. - chunks = (function(chunks) { - var j = 0, - skip = /[^"'`\{\}\/]+/g, - comment = /\/\*(?:[^*]|\*+[^\/*])*\*+\/|\/\/.*/g, - level = 0, - match, - chunk = chunks[0], - inString; - - chunker: for (var i = 0, c, cc; i < input.length; i++) { - skip.lastIndex = i; - if (match = skip.exec(input)) { - if (match.index === i) { - i += match[0].length; - chunk.push(match[0]); - } - } - c = input.charAt(i); - comment.lastIndex = i; - - if (!inString && c === '/') { - cc = input.charAt(i + 1); - if (cc === '/' || cc === '*') { - if (match = comment.exec(input)) { - if (match.index === i) { - i += match[0].length - 1; - chunk.push(match[0]); - c = input.charAt(i); - continue chunker; - } - } - } - } - - if (c === '{' && !inString) { level++; - chunk.push(c); - } else if (c === '}' && !inString) { level--; - chunk.push(c); - chunks[++j] = chunk = []; - } else { - if (c === '"' || c === "'" || c === '`') { - if (! inString) { - inString = c; - } else { - inString = inString === c ? false : inString; - } - } - chunk.push(c); - } - } - if (level > 0) { - // TODO: make invalid instead - throw makeError({ - message: 'Missing closing `}`', - index: i - }); - } - - return chunks.map(function(c) { return c.join(''); }); - })([[]]); - - // Start with the primary rule. - // The whole syntax tree is held under a Ruleset node, - // with the `root` property set to true, so no `{}` are - // output. The callback is called when the input is parsed. - root = new tree.Ruleset([], $(this.parsers.primary)); - root.root = true; - - // Get an array of Ruleset objects, flattened - // and sorted according to specificitySort - root.toList = (function() { - var line, lines, column; - return function(env) { - env.error = function(e) { - if (!env.errors) env.errors = new Error(''); - if (env.errors.message) { - env.errors.message += '\n' + makeError(e).message; - } else { - env.errors.message = makeError(e).message; - } - }; - env.frames = env.frames || []; - - // call populates Invalid-caused errors - var definitions = this.flatten([], [], env); - definitions.sort(specificitySort); - return definitions; - }; - })(); - - // Sort rules by specificity: this function expects selectors to be - // split already. - // - // Written to be used as a .sort(Function); - // argument. - // - // [1, 0, 0, 467] > [0, 0, 1, 520] - var specificitySort = function(a, b) { - var as = a.specificity; - var bs = b.specificity; - - if (as[0] != bs[0]) return bs[0] - as[0]; - if (as[1] != bs[1]) return bs[1] - as[1]; - if (as[2] != bs[2]) return bs[2] - as[2]; - return bs[3] - as[3]; - }; - - // If `i` is smaller than the `input.length - 1`, - // it means the parser wasn't able to parse the whole - // string, so we've got a parsing error. - // - // We try to extract a \n delimited string, - // showing the line where the parse error occured. - // We split it up into two parts (the part which parsed, - // and the part which didn't), so we can color them differently. - if (i < input.length - 1) throw makeError({ - message:'Parse error.', - index:i - }); - - return root; - }, - - // - // Here in, the parsing rules/functions - // - // The basic structure of the syntax tree generated is as follows: - // - // Ruleset -> Rule -> Value -> Expression -> Entity - // - // In general, most rules will try to parse a token with the `$()` function, and if the return - // value is truly, will return a new node, of the relevant type. Sometimes, we need to check - // first, before parsing, that's when we use `peek()`. - // - parsers: { - // - // The `primary` rule is the *entry* and *exit* point of the parser. - // The rules here can appear at any level of the parse tree. - // - // The recursive nature of the grammar is an interplay between the `block` - // rule, which represents `{ ... }`, the `ruleset` rule, and this `primary` rule, - // as represented by this simplified grammar: - // - // primary → (ruleset | rule)+ - // ruleset → selector+ block - // block → '{' primary '}' - // - // Only at one point is the primary rule not called from the - // block rule: at the root level. - // - primary: function() { - var node, root = []; - - while ((node = $(this.rule) || $(this.ruleset) || - $(this.comment)) || - $(/^[\s\n]+/) || (node = $(this.invalid))) { - node && root.push(node); - } - return root; - }, - - invalid: function () { - var chunk = $(/^[^;\n]*[;\n]/); - - // To fail gracefully, match everything until a semicolon or linebreak. - if (chunk) { - return new(tree.Invalid)(chunk, memo); - } - }, - - // We create a Comment node for CSS comments `/* */`, - // but keep the LeSS comments `//` silent, by just skipping - // over them. - comment: function() { - var comment; - - if (input.charAt(i) !== '/') return; - - if (input.charAt(i + 1) === '/') { - return new tree.Comment($(/^\/\/.*/), true); - } else if (comment = $(/^\/\*(?:[^*]|\*+[^\/*])*\*+\/\n?/)) { - return new tree.Comment(comment); - } - }, - - // - // Entities are tokens which can be found inside an Expression - // - entities: { - // - // A string, which supports escaping " and ' - // - // "milky way" 'he\'s the one!' - // - quoted: function() { - if (input.charAt(i) !== '"' && input.charAt(i) !== "'") return; - var str = $(/^"((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)'/); - if (str) { - return new tree.Quoted(str[1] || str[2]); - } - }, - - // A reference to a Mapnik field, like - // - // [NAME] - // - // Behind the scenes, this has the same representation, but Carto - // needs to be careful to warn when unsupported operations are used. - field: function() { - if (! $('[')) return; - var field_name = $(/(^[a-zA-Z0-9\-_]+)/); - if (! $(']')) return; - if (field_name) return new tree.Field(field_name[1]); - }, - - // This is a comparison operator - comparison: function() { - var str = $(/^=~|=|!=|<=|>=|<|>/); - if (str) { - return str; - } - }, - - // A catch-all word, such as: - // - // hard-light - // - // These can start with either a letter or a dash (-), - // and then contain numbers, underscores, and letters. - keyword: function() { - var k = $(/^[A-Za-z-]+[A-Za-z-0-9_]*/); - if (k) { return new tree.Keyword(k); } - }, - - // A function call - // - // rgb(255, 0, 255) - // - // The arguments are parsed with the `entities.arguments` parser. - call: function() { - var name, args; - - if (! (name = /^([\w\-]+|%)\(/.exec(chunks[j]))) return; - - name = name[1].toLowerCase(); - - if (name === 'url') { - return null; - } else { - i += name.length + 1; - } - - args = $(this.entities.arguments); - - if (!$(')')) return; - - if (name) { - return new tree.Call(name, args, i); - } - }, - // Arguments are comma-separated expressions - 'arguments': function() { - var args = [], arg; - - while (arg = $(this.expression)) { - args.push(arg); - if (! $(',')) { break; } - } - - return args; - }, - literal: function() { - return $(this.entities.dimension) || - $(this.entities.color) || - $(this.entities.quoted); - }, - - // Parse url() tokens - // - // We use a specific rule for urls, because they don't really behave like - // standard function calls. The difference is that the argument doesn't have - // to be enclosed within a string, so it can't be parsed as an Expression. - url: function() { - var value; - - if (input.charAt(i) !== 'u' || !$(/^url\(/)) return; - value = $(this.entities.quoted) || $(this.entities.variable) || - $(/^[\-\w%@$\/.&=:;#+?~]+/) || ''; - if (! $(')')) { - return new tree.Invalid(value, memo, 'Missing closing ) in URL.'); - } else { - return new tree.URL((value.value || value instanceof tree.Variable) ? - value : new tree.Quoted(value), imports.paths); - } - }, - - // A Variable entity, such as `@fink`, in - // - // width: @fink + 2px - // - // We use a different parser for variable definitions, - // see `parsers.variable`. - variable: function() { - var name, index = i; - - if (input.charAt(i) === '@' && (name = $(/^@[\w-]+/))) { - return new tree.Variable(name, index, env.filename); - } - }, - - // A Hexadecimal color - // - // #4F3C2F - // - // `rgb` and `hsl` colors are parsed through the `entities.call` parser. - color: function() { - var rgb; - - if (input.charAt(i) === '#' && (rgb = $(/^#([a-fA-F0-9]{6}|[a-fA-F0-9]{3})/))) { - return new tree.Color(rgb[1]); - } else { - rgb = chunks[j].match(/^[a-z]+/); - if (rgb && rgb[0] in tree.Reference.data.colors) { - return new tree.Color(tree.Reference.data.colors[$(/^[a-z]+/)]); - } - } - }, - - // A Dimension, that is, a number and a unit. The only - // unit that has an effect is % - // - // 0.5em 95% - dimension: function() { - var c = input.charCodeAt(i); - if ((c > 57 || c < 45) || c === 47) return; - var value = $(/^(-?\d*\.?\d+)(\%|\w+)?/); - if (value) { - return new tree.Dimension(value[1], value[2], memo); - } - } - }, - - // The variable part of a variable definition. Used in the `rule` parser - // - // @fink: - variable: function() { - var name; - - if (input.charAt(i) === '@' && (name = $(/^(@[\w-]+)\s*:/))) { - return name[1]; - } - }, - - // - // Entities are the smallest recognized token, - // and can be found inside a rule's value. - // - entity: function() { - return $(this.entities.literal) || - $(this.entities.field) || - $(this.entities.variable) || - $(this.entities.url) || - $(this.entities.call) || - $(this.entities.keyword); - }, - - // - // A Rule terminator. Note that we use `peek()` to check for '}', - // because the `block` rule will be expecting it, but we still need to make sure - // it's there, if ';' was ommitted. - // - end: function() { - return $(';') || peek('}'); - }, - - // Elements are the building blocks for Selectors. They consist of - // an element name, such as a tag a class, or `*`. - element: function() { - var e = $(/^(?:[.#][\w\-]+|\*|Map)/); - if (e) return new tree.Element(e); - }, - - // Attachments allow adding multiple lines, polygons etc. to an - // object. There can only be one attachment per selector. - attachment: function() { - var s = $(/^::([\w\-]+(?:\/[\w\-]+)*)/); - if (s) return s[1]; - }, - - // Selectors are made out of one or more Elements, see above. - selector: function() { - var a, attachment; - var e, elements = []; - var f, filters = new tree.Filterset(); - var z, zoom = tree.Zoom.all; - var fo, frame_offset = tree.FrameOffset.none; - var segments = 0, conditions = 0; - - while ( - (e = $(this.element)) || - (z = $(this.zoom)) || - (fo = $(this.frame_offset)) || - (f = $(this.filter)) || - (a = $(this.attachment)) - ) { - segments++; - if (e) { - elements.push(e); - } else if (z) { - zoom &= z; - conditions++; - } else if (fo) { - frame_offset = fo; - conditions++; - } else if (f) { - filters.add(f); - conditions++; - } else if (attachment) { - throw makeError({ - message:'Encountered second attachment name.', - index:i - 1 - }); - } else { - attachment = a; - } - - var c = input.charAt(i); - if (c === '{' || c === '}' || c === ';' || c === ',') { break; } - } - - if (segments) { - return new tree.Selector(filters, zoom, frame_offset, elements, attachment, conditions, memo); - } - }, - - filter: function() { - save(); - var key, op, val; - if (! $('[')) return; - if (key = $(/^[a-zA-Z0-9\-_]+/) || $(this.entities.quoted) || $(this.entities.variable)) { - if ((op = $(this.entities.comparison)) && - (val = $(this.entities.quoted) || $(this.entities.variable) || $(/^[\w\-\.]+/))) { - if (! $(']')) return; - return new tree.Filter(key, op, val, memo, env.filename); - } - } - }, - - frame_offset: function() { - save(); - var op, val; - if ($(/^\[\s*frame-offset/g) && - (op = $(this.entities.comparison)) && - (val = $(/^\d+/)) && - $(']')) { - return tree.FrameOffset(op, val, memo); - } - }, - - zoom: function() { - save(); - var op, val; - if ($(/^\[\s*zoom/g) && - (op = $(this.entities.comparison)) && - (val = $(/^\d+/)) && - $(']')) { - return tree.Zoom(op, val, memo); - } - }, - - // - // The `block` rule is used by `ruleset` - // It's a wrapper around the `primary` rule, with added `{}`. - // - block: function() { - var content; - - if ($('{') && (content = $(this.primary)) && $('}')) { - return content; - } - }, - - // - // div, .class, body > p {...} - // - ruleset: function() { - var selectors = [], s, f, l, rules, filters = []; - save(); - - while (s = $(this.selector)) { - selectors.push(s); - if (! $(',')) { break } - } - if (s) $(this.comment); - - if (selectors.length > 0 && (rules = $(this.block))) { - if (selectors.length === 1 && - selectors[0].elements.length && - selectors[0].elements[0].value === 'Map') { - var rs = new tree.Ruleset(selectors, rules); - rs.isMap = true; - return rs; - } - return new tree.Ruleset(selectors, rules); - } else { - // Backtrack - restore(); - } - }, - rule: function() { - var name, value, c = input.charAt(i); - save(); - - if (c === '.' || c === '#' || c === '&') { return } - - if (name = $(this.variable) || $(this.property)) { - value = $(this.value); - - if (value && $(this.end)) { - return new tree.Rule(name, value, memo, env.filename); - } else { - furthest = i; - restore(); - } - } - }, - - font: function() { - var value = [], expression = [], weight, font, e; - - while (e = $(this.entity)) { - expression.push(e); - } - - value.push(new tree.Expression(expression)); - - if ($(',')) { - while (e = $(this.expression)) { - value.push(e); - if (! $(',')) { break; } - } - } - return new tree.Value(value); - }, - - // A Value is a comma-delimited list of Expressions - // In a Rule, a Value represents everything after the `:`, - // and before the `;`. - value: function() { - var e, expressions = []; - - while (e = $(this.expression)) { - expressions.push(e); - if (! $(',')) { break; } - } - - if (expressions.length > 0) { - return new tree.Value(expressions); - } - }, - // A sub-expression, contained by parenthensis - sub: function() { - var e; - - if ($('(') && (e = $(this.expression)) && $(')')) { - return e; - } - }, - // This is a misnomer because it actually handles multiplication - // and division. - multiplication: function() { - var m, a, op, operation; - if (m = $(this.operand)) { - while ((op = ($('/') || $('*') || $('%'))) && (a = $(this.operand))) { - operation = new tree.Operation(op, [operation || m, a], memo); - } - return operation || m; - } - }, - addition: function() { - var m, a, op, operation; - if (m = $(this.multiplication)) { - while ((op = $(/^[-+]\s+/) || (input.charAt(i - 1) != ' ' && ($('+') || $('-')))) && - (a = $(this.multiplication))) { - operation = new tree.Operation(op, [operation || m, a], memo); - } - return operation || m; - } - }, - - // An operand is anything that can be part of an operation, - // such as a Color, or a Variable - operand: function() { - return $(this.sub) || $(this.entity); - }, - - // Expressions either represent mathematical operations, - // or white-space delimited Entities. - // - // 1px solid black - // @var * 2 - expression: function() { - var e, delim, entities = [], d; - - while (e = $(this.addition) || $(this.entity)) { - entities.push(e); - } - if (entities.length > 0) { - return new tree.Expression(entities); - } - }, - property: function() { - var name = $(/^(([a-z][-a-z_0-9]*\/)?\*?-?[-a-z_0-9]+)\s*:/); - if (name) return name[1]; - } - } - }; -}; - -/** - * TODO: document this. What does this do? - */ -if(typeof(module) !== "undefined") { - module.exports.find = function (obj, fun) { - for (var i = 0, r; i < obj.length; i++) { - if (r = fun.call(obj, obj[i])) { return r; } - } - return null; - }; -} -(function(tree) { - -tree.Anonymous = function Anonymous(string) { - this.value = string.value || string; -}; -tree.Anonymous.prototype = { - toString: function() { - return this.value; - }, - eval: function() { return this; } -}; - -})(require('../tree')); -(function(tree) { - -tree.Call = function Call(name, args, index) { - this.is = 'call'; - - this.name = name; - this.args = args; - this.index = index; -}; - -tree.Call.prototype = { - // - // When evaluating a function call, - // we either find the function in `tree.functions` [1], - // in which case we call it, passing the evaluated arguments, - // or we simply print it out as it appeared originally [2]. - // - // The *functions.js* file contains the built-in functions. - // - // The reason why we evaluate the arguments, is in the case where - // we try to pass a variable to a function, like: `saturate(@color)`. - // The function should receive the value, not the variable. - // - eval: function(env) { - var args = this.args.map(function(a) { return a.eval(env); }); - - for (var i = 0; i < args.length; i++) { - if (args[i].is === 'undefined') { - return { - is: 'undefined', - value: 'undefined' - }; - } - } - - if (this.name in tree.functions) { - if (tree.functions[this.name].length === args.length) { - return tree.functions[this.name].apply(tree.functions, args); - } else { - env.error({ - message: 'incorrect number of arguments for ' + this.name + - '(). ' + tree.functions[this.name].length + ' expected.', - index: this.index, - type: 'runtime', - filename: this.filename - }); - return { - is: 'undefined', - value: 'undefined' - }; - } - } else { - var fn = tree.Reference.mapnikFunction(this.name); - if (!fn) { - env.error({ - message: 'unknown function ' + this.name, - index: this.index, - type: 'runtime', - filename: this.filename - }); - return { - is: 'undefined', - value: 'undefined' - }; - } - if (fn[1] !== args.length) { - env.error({ - message: 'function ' + this.name + ' takes ' + - fn[1] + ' arguments and was given ' + args.length, - index: this.index, - type: 'runtime', - filename: this.filename - }); - return { - is: 'undefined', - value: 'undefined' - }; - } else { - // Save the evaluated versions of arguments - this.args = args; - return this; - } - } - }, - - toString: function(env, format) { - if (format === 'image-filter') { - if (this.args.length) { - return this.name + ':' + this.args.join(','); - } else { - return this.name; - } - } else { - if (this.args.length) { - return this.name + '(' + this.args.join(',') + ')'; - } else { - return this.name; - } - } - } -}; - -})(require('../tree')); -(function(tree) { -// -// RGB Colors - #ff0014, #eee -// -tree.Color = function Color(rgb, a) { - // - // The end goal here, is to parse the arguments - // into an integer triplet, such as `128, 255, 0` - // - // This facilitates operations and conversions. - // - if (Array.isArray(rgb)) { - this.rgb = rgb; - } else if (rgb.length == 6) { - this.rgb = rgb.match(/.{2}/g).map(function(c) { - return parseInt(c, 16); - }); - } else { - this.rgb = rgb.split('').map(function(c) { - return parseInt(c + c, 16); - }); - } - this.is = 'color'; - this.alpha = typeof(a) === 'number' ? a : 1; -}; - -tree.Color.prototype = { - eval: function() { return this; }, - - // If we have some transparency, the only way to represent it - // is via `rgba`. Otherwise, we use the hex representation, - // which has better compatibility with older browsers. - // Values are capped between `0` and `255`, rounded and zero-padded. - toString: function() { - if (this.alpha < 1.0) { - return 'rgba(' + this.rgb.map(function(c) { - return Math.round(c); - }).concat(this.alpha).join(', ') + ')'; - } else { - return '#' + this.rgb.map(function(i) { - i = Math.round(i); - i = (i > 255 ? 255 : (i < 0 ? 0 : i)).toString(16); - return i.length === 1 ? '0' + i : i; - }).join(''); - } - }, - - // Operations have to be done per-channel, if not, - // channels will spill onto each other. Once we have - // our result, in the form of an integer triplet, - // we create a new Color node to hold the result. - operate: function(op, other) { - var result = []; - - if (! (other instanceof tree.Color)) { - other = other.toColor(); - } - - for (var c = 0; c < 3; c++) { - result[c] = tree.operate(op, this.rgb[c], other.rgb[c]); - } - return new tree.Color(result); - }, - - toHSL: function() { - var r = this.rgb[0] / 255, - g = this.rgb[1] / 255, - b = this.rgb[2] / 255, - a = this.alpha; - - var max = Math.max(r, g, b), min = Math.min(r, g, b); - var h, s, l = (max + min) / 2, d = max - min; - - if (max === min) { - h = s = 0; - } else { - s = l > 0.5 ? d / (2 - max - min) : d / (max + min); - - switch (max) { - case r: h = (g - b) / d + (g < b ? 6 : 0); break; - case g: h = (b - r) / d + 2; break; - case b: h = (r - g) / d + 4; break; - } - h /= 6; - } - return { h: h * 360, s: s, l: l, a: a }; - } -}; - - -})(require('../tree')); -(function(tree) { - -tree.Comment = function Comment(value, silent) { - this.value = value; - this.silent = !!silent; -}; - -tree.Comment.prototype = { - toString: function(env) { - return ''; - }, - eval: function() { return this; } -}; - -})(require('../tree')); -(function(tree) { -var assert = require('assert'); - -tree.Definition = function Definition(selector, rules) { - this.elements = selector.elements; - assert.ok(selector.filters instanceof tree.Filterset); - this.rules = rules; - this.ruleIndex = []; - for (var i = 0; i < this.rules.length; i++) { - if ('zoom' in this.rules[i]) this.rules[i] = this.rules[i].clone(); - this.rules[i].zoom = selector.zoom; - this.ruleIndex.push(this.rules[i].updateID()); - } - this.filters = selector.filters; - this.zoom = selector.zoom; - this.frame_offset = selector.frame_offset; - this.attachment = selector.attachment || '__default__'; - this.specificity = selector.specificity(); -}; - -tree.Definition.prototype.toString = function() { - var str = this.filters.toString(); - for (var i = 0; i < this.rules.length; i++) { - str += '\n ' + this.rules[i]; - } - return str; -}; - -tree.Definition.prototype.clone = function(filters) { - if (filters) assert.ok(filters instanceof tree.Filterset); - var clone = Object.create(tree.Definition.prototype); - clone.rules = this.rules.slice(); - clone.ruleIndex = this.ruleIndex.slice(); - clone.filters = filters ? filters : this.filters.clone(); - clone.attachment = this.attachment; - return clone; -}; - -tree.Definition.prototype.addRules = function(rules) { - var added = 0; - - // Add only unique rules. - for (var i = 0; i < rules.length; i++) { - if (this.ruleIndex.indexOf(rules[i].id) < 0) { - this.rules.push(rules[i]); - this.ruleIndex.push(rules[i].id); - added++; - } - } - - return added; -}; - -/** - * Determine whether this selector matches a given id - * and array of classes, by determining whether - * all elements it contains match. - */ -tree.Definition.prototype.appliesTo = function(id, classes) { - for (var i = 0; i < this.elements.length; i++) { - if (!this.elements[i].matches(id, classes)) { - return false; - } - } - return true; -}; - -tree.Definition.prototype.symbolizersToXML = function(env, symbolizers, zoom) { - var xml = ' \n'; - xml += tree.Zoom.toXML(zoom).join(''); - xml += this.filters.toXML(env); - - // Sort symbolizers by the index of their first property definition - var sym_order = [], indexes = []; - for (var key in symbolizers) { - indexes = []; - for (var prop in symbolizers[key]) { - indexes.push(symbolizers[key][prop].index); - } - var min_idx = Math.min.apply(Math, indexes); - sym_order.push([key, min_idx]); - } - - // Get a simple list of the symbolizers, in order - sym_order = sym_order.sort(function(a, b) { - return a[1] - b[1]; - }).map(function(v) { - return v[0]; - }); - - for (var i = 0; i < sym_order.length; i++) { - var attributes = symbolizers[sym_order[i]]; - var symbolizer = sym_order[i].split('/').pop(); - - // Skip the magical * symbolizer which is used for universal properties - // which are bubbled up to Style elements intead of Symbolizer elements. - if (symbolizer === '*') continue; - - var fail = tree.Reference.requiredProperties(symbolizer, attributes); - if (fail) { - var rule = attributes[Object.keys(attributes).shift()]; - env.error({ - message: fail, - index: rule.index, - filename: rule.filename - }); - } - - var name = symbolizer.charAt(0).toUpperCase() + - symbolizer.slice(1).replace(/\-./, function(str) { - return str[1].toUpperCase(); - }) + 'Symbolizer'; - - var selfclosing = true, tagcontent; - xml += ' <' + name + ' '; - for (var key in attributes) { - if (symbolizer === 'map') env.error({ - message: 'Map properties are not permitted in other rules', - index: attributes[key].index, - filename: attributes[key].filename - }); - var x = tree.Reference.selector(attributes[key].name); - if (x && x.serialization && x.serialization === 'content') { - selfclosing = false; - tagcontent = attributes[key].eval(env).toXML(env, true); - } else { - xml += attributes[key].eval(env).toXML(env) + ' '; - } - } - if (selfclosing) { - xml += '/>\n'; - } else { - xml += '>\n'; - } - } - xml += ' \n'; - return xml; -}; - -tree.Definition.prototype.collectSymbolizers = function(zooms, i) { - var symbolizers = {}, child; - - for (var j = i; j < this.rules.length; j++) { - child = this.rules[j]; - var key = child.instance + '/' + child.symbolizer; - if (zooms.current & child.zoom && - (!(key in symbolizers) || - (!(child.name in symbolizers[key])))) { - zooms.current &= child.zoom; - if (!(key in symbolizers)) { - symbolizers[key] = {}; - } - symbolizers[key][child.name] = child; - } - } - - if (Object.keys(symbolizers).length) { - zooms.rule &= (zooms.available &= ~zooms.current); - return symbolizers; - } -}; - -tree.Definition.prototype.toXML = function(env, existing) { - // The tree.Zoom.toString function ignores the holes in zoom ranges and outputs - // scaledenominators that cover the whole range from the first to last bit set. - // This algorithm can produces zoom ranges that may have holes. However, - // when using the filter-mode="first", more specific zoom filters will always - // end up before broader ranges. The filter-mode will pick those first before - // resorting to the zoom range with the hole and stop processing further rules. - var filter = this.filters.toString(); - if (!(filter in existing)) existing[filter] = tree.Zoom.all; - - var available = tree.Zoom.all, xml = '', zoom, symbolizers; - var zooms = { available: tree.Zoom.all }; - for (var i = 0; i < this.rules.length && available; i++) { - zooms.rule = this.rules[i].zoom; - if (!(existing[filter] & zooms.rule)) continue; - - while (zooms.current = zooms.rule & available) { - if (symbolizers = this.collectSymbolizers(zooms, i)) { - if (!(existing[filter] & zooms.current)) continue; - xml += this.symbolizersToXML(env, symbolizers, existing[filter] & zooms.current); - existing[filter] &= ~zooms.current; - } - } - } - - return xml; -}; - -})(require('../tree')); -(function(tree) { - -// -// A number with a unit -// -tree.Dimension = function Dimension(value, unit, index) { - this.value = parseFloat(value); - this.unit = unit || null; - this.is = 'float'; - this.index = index; -}; - -tree.Dimension.prototype = { - eval: function (env) { - if (this.unit && ['px', '%'].indexOf(this.unit) === -1) { - env.error({ - message: "Invalid unit: '" + this.unit + "'", - index: this.index - }); - } - - return this; - }, - round: function() { - this.value = Math.round(this.value); - return this; - }, - toColor: function() { - return new tree.Color([this.value, this.value, this.value]); - }, - toString: function() { - return this.value.toString(); - }, - - // In an operation between two Dimensions, - // we default to the first Dimension's unit, - // so `1px + 2em` will yield `3px`. - // In the future, we could implement some unit - // conversions such that `100cm + 10mm` would yield - // `101cm`. - operate: function(op, other) { - return new tree.Dimension(tree.operate(op, this.value, other.value), - this.unit || other.unit); - } -}; - -})(require('../tree')); -(function(tree) { - -tree.Directive = function Directive(name, value) { - this.name = name; - if (Array.isArray(value)) { - this.ruleset = new tree.Ruleset([], value); - } else { - this.value = value; - } -}; -tree.Directive.prototype = { - toString: function(ctx, env) { - if (this.ruleset) { - this.ruleset.root = true; - return this.name + ' {\n ' + - this.ruleset.toString(ctx, env).trim().replace(/\n/g, '\n ') + - '\n}\n'; - } else { - return this.name + ' ' + this.value.toString() + ';\n'; - } - }, - eval: function(env) { - env.frames.unshift(this); - this.ruleset = this.ruleset && this.ruleset.eval(env); - env.frames.shift(); - return this; - }, - variable: function(name) { - return tree.Ruleset.prototype.variable.call(this.ruleset, name); - }, - find: function() { - return tree.Ruleset.prototype.find.apply(this.ruleset, arguments); - }, - rulesets: function() { - return tree.Ruleset.prototype.rulesets.apply(this.ruleset); - } -}; - -})(require('../tree')); -(function(tree) { - -// An element is an id or class selector -tree.Element = function Element(value) { - this.value = value.trim(); -}; - -// Determine the 'specificity matrix' of this -// specific selector -tree.Element.prototype.specificity = function() { - return [ - (this.value[0] == '#') ? 1 : 0, // a - (this.value[0] == '.') ? 1 : 0 // b - ]; -}; - -tree.Element.prototype.toString = function() { - return this.value; -}; - -// Determine whether this element matches an id or classes. -// An element is a single id or class, or check whether the given -// array of classes contains this, or the id is equal to this. -// -// Takes a plain string for id and plain strings in the array of -// classes. -tree.Element.prototype.matches = function(id, classes) { - return (classes.indexOf(this.value.replace(/^\./, '')) !== -1) || - (this.value.replace(/^#/, '') === id) || - (this.value === '*'); -}; - -})(require('../tree')); -(function(tree) { - -tree.Expression = function Expression(value) { - this.value = value; -}; -tree.Expression.prototype = { - eval: function(env) { - if (this.value.length > 1) { - return new tree.Expression(this.value.map(function(e) { - return e.eval(env); - })); - } else { - return this.value[0].eval(env); - } - }, - toString: function(env) { - return this.value.map(function(e) { - return e.toString(env); - }).join(' '); - } -}; - -})(require('../tree')); -(function(tree) { - -tree.Field = function Field(content) { - this.value = content || ''; - this.is = 'field'; -}; - -tree.Field.prototype = { - toString: function() { - return '[' + this.value + ']'; - }, - 'eval': function() { - return this; - } -}; - -})(require('../tree')); -(function(tree) { - -tree.Filter = function Filter(key, op, val, index, filename) { - if (key.is) { - this.key = key.value; - this._key = key; - } else { - this.key = key; - } - - this.op = op; - this.index = index; - this.filename = filename; - - if (val.is) { - this.val = val.value; - this._val = val; - } else { - this.val = val; - } - - if (ops[this.op][1] == 'numeric') { - this.val = 1 * this.val; - } - - this.id = this.key + this.op + this.val; -}; - - -// xmlsafe, numeric, suffix -var ops = { - '<': [' < ', 'numeric'], - '>': [' > ', 'numeric'], - '=': [' = ', 'both'], - '!=': [' != ', 'both'], - '<=': [' <= ', 'numeric'], - '>=': [' >= ', 'numeric'], - '=~': ['.match(', 'string', ')'] -}; - -tree.Filter.prototype.toXML = function(env) { - if (this.val.eval) this._val = this.val.eval(env); - if (this.key.eval) this._key = this.key.eval(env); - if (this._key) var key = this._key.toString(false); - if (this._val) var val = this._val.toString(this._val.is == 'string'); - - if ( - (ops[this.op][1] == 'numeric' && isNaN(this.val)) || - (ops[this.op][1] == 'string' && (val || this.val)[0] != "'") - ) { - env.error({ - message: 'Cannot use operator "' + this.op + '" with value ' + this.val, - index: this.index, - filename: this.filename - }); - } - - return '[' + (key || this.key) + ']' + ops[this.op][0] + '' + (val || this.val) + (ops[this.op][2] || ''); -}; - -tree.Filter.prototype.toString = function() { - return '[' + this.id + ']'; -}; - -})(require('../tree')); -var tree = require('../tree'); - -tree.Filterset = function Filterset() {}; - -Object.defineProperty(tree.Filterset.prototype, 'toXML', { - enumerable: false, - value: function(env) { - var filters = []; - for (var id in this) { - filters.push('(' + this[id].toXML(env).trim() + ')'); - } - - if (filters.length) { - return ' ' + filters.join(' and ') + '\n'; - } else { - return ''; - } - } -}); - -Object.defineProperty(tree.Filterset.prototype, 'toString', { - enumerable: false, - value: function() { - var arr = []; - for (var id in this) arr.push(this[id].id); - arr.sort(); - return arr.join('\t'); - } -}); - -Object.defineProperty(tree.Filterset.prototype, 'clone', { - enumerable: false, - value: function() { - var clone = new tree.Filterset(); - for (var id in this) { - clone[id] = this[id]; - } - return clone; - } -}); - -// Note: other has to be a tree.Filterset. -Object.defineProperty(tree.Filterset.prototype, 'cloneWith', { - enumerable: false, - value: function(other) { - var additions; - for (var id in other) { - var status = this.addable(other[id]); - if (status === false) { - return false; - } - if (status === true) { - // Adding the filter will override another value. - if (!additions) additions = []; - additions.push(other[id]); - } - } - - // Adding the other filters doesn't make this filterset invalid, but it - // doesn't add anything to it either. - if (!additions) return null; - - // We can successfully add all filters. Now clone the filterset and add the - // new rules. - var clone = new tree.Filterset(); - - // We can add the rules that are already present without going through the - // add function as a Filterset is always in it's simplest canonical form. - for (var id in this) { - clone[id] = this[id]; - } - - // Only add new filters that actually change the filter. - while (id = additions.shift()) { - clone.add(id); - } - - return clone; - } -}); - -/** - * Returns true when the new filter can be added, false otherwise. - */ -Object.defineProperty(tree.Filterset.prototype, 'addable', { - enumerable: false, - value: function(filter) { - var key = filter.key, value = filter.val; - - switch (filter.op) { - case '=': - if (key + '=' in this) return (this[key + '='].val != value) ? false : null; - if (key + '!=' + value in this) return false; - if (key + '>' in this && this[key + '>'].val >= value) return false; - if (key + '<' in this && this[key + '<'].val <= value) return false; - if (key + '>=' in this && this[key + '>='].val > value) return false; - if (key + '<=' in this && this[key + '<='].val < value) return false; - return true; - - case '!=': - if (key + '=' in this) return (this[key + '='].val == value) ? false : null; - if (key + '!=' + value in this) return null; - if (key + '>' in this && this[key + '>'].val >= value) return null; - if (key + '<' in this && this[key + '<'].val <= value) return null; - if (key + '>=' in this && this[key + '>='].val > value) return null; - if (key + '<=' in this && this[key + '<='].val < value) return null; - return true; - - case '>': - if (key + '=' in this) return (this[key + '='].val <= value) ? false : null; - if (key + '<' in this && this[key + '<'].val <= value) return false; - if (key + '<=' in this && this[key + '<='].val <= value) return false; - if (key + '>' in this && this[key + '>'].val >= value) return null; - if (key + '>=' in this && this[key + '>='].val > value) return null; - return true; - - case '>=': - if (key + '=' in this) return (this[key + '='].val < value) ? false : null; - if (key + '<' in this && this[key + '<'].val <= value) return false; - if (key + '<=' in this && this[key + '<='].val < value) return false; - if (key + '>' in this && this[key + '>'].val >= value) return null; - if (key + '>=' in this && this[key + '>='].val >= value) return null; - return true; - - case '<': - if (key + '=' in this) return (this[key + '='].val >= value) ? false : null; - if (key + '>' in this && this[key + '>'].val >= value) return false; - if (key + '>=' in this && this[key + '>='].val >= value) return false; - if (key + '<' in this && this[key + '<'].val <= value) return null; - if (key + '<=' in this && this[key + '<='].val < value) return null; - return true; - - case '<=': - if (key + '=' in this) return (this[key + '='].val > value) ? false : null; - if (key + '>' in this && this[key + '>'].val >= value) return false; - if (key + '>=' in this && this[key + '>='].val > value) return false; - if (key + '<' in this && this[key + '<'].val <= value) return null; - if (key + '<=' in this && this[key + '<='].val <= value) return null; - return true; - } - } -}); - -/** - * Only call this function for filters that have been cleared by .addable(). - */ -Object.defineProperty(tree.Filterset.prototype, 'add', { - enumerable: false, - value: function(filter) { - var key = filter.key; - - switch (filter.op) { - case '=': - for (var id in this) { - if (this[id].key == key) { - delete this[id]; - } - } - this[key + '='] = filter; - break; - - case '!=': - this[key + '!=' + filter.val] = filter; - break; - - case '=~': - this[key + '=~' + filter.val] = filter; - break; - - case '>': - // If there are other filters that are also > - // but are less than this one, they don't matter, so - // remove them. - for (var id in this) { - if (this[id].key == key && this[id].val <= filter.val) { - delete this[id]; - } - } - this[key + '>'] = filter; - break; - - case '>=': - for (var id in this) { - if (this[id].key == key && this[id].val < filter.val) { - delete this[id]; - } - } - if (key + '!=' + filter.val in this) { - delete this[key + '!=' + filter.val]; - filter.op = '>'; - this[key + '>'] = filter; - } - else { - this[key + '>='] = filter; - } - break; - - case '<': - for (var id in this) { - if (this[id].key == key && this[id].val >= filter.val) { - delete this[id]; - } - } - this[key + '<'] = filter; - break; - - case '<=': - for (var id in this) { - if (this[id].key == key && this[id].val > filter.val) { - delete this[id]; - } - } - if (key + '!=' + filter.val in this) { - delete this[key + '!=' + filter.val]; - filter.op = '<'; - this[key + '<'] = filter; - } - else { - this[key + '<='] = filter; - } - break; - } - } -}); -(function(tree) { - -tree._getFontSet = function(env, fonts) { - var find_existing = function(fonts) { - var findFonts = fonts.join(''); - for (var i = 0; i < env.effects.length; i++) { - if (findFonts == env.effects[i].fonts.join('')) { - return env.effects[i]; - } - } - }; - - var existing = false; - if (existing = find_existing(fonts)) { - return existing; - } else { - var new_fontset = new tree.FontSet(env, fonts); - env.effects.push(new_fontset); - return new_fontset; - } -}; - -tree.FontSet = function FontSet(env, fonts) { - this.fonts = fonts; - this.name = 'fontset-' + env.effects.length; -}; - -tree.FontSet.prototype.toXML = function(env) { - return '\n' + - this.fonts.map(function(f) { - return ' '; - }).join('\n') + - '\n'; -}; - -})(require('../tree')); -var tree = require('../tree'); - -// Storage for Frame offset value -// and stores them as bit-sequences so that they can be combined, -// inverted, and compared quickly. -tree.FrameOffset = function(op, value, index) { - value = parseInt(value, 10); - if (value > tree.FrameOffset.max || value <= 0) { - throw { - message: 'Only frame-offset levels between 1 and ' + - tree.FrameOffset.max + ' supported.', - index: index - }; - } - - if (op !== '=') { - throw { - message: 'only = operator is supported for frame-offset', - index: index - }; - } - return value; -}; - -tree.FrameOffset.max = 32; -tree.FrameOffset.none = 0; - -(function(tree) { -// -// RGB Colors - #ff0014, #eee -// -tree.ImageFilter = function ImageFilter(filter, args) { - this.is = 'imagefilter'; - this.filter = filter; - this.args = args || null; -}; -tree.ImageFilter.prototype = { - eval: function() { return this; }, - - toString: function() { - if (this.args) { - return this.filter + ':' + this.args.join(','); - } else { - return this.filter; - } - } -}; - - -})(require('../tree')); -(function (tree) { -tree.Invalid = function Invalid(chunk, index, message) { - this.chunk = chunk; - this.index = index; - this.type = 'syntax'; - this.message = message || "Invalid code: " + this.chunk; -}; - -tree.Invalid.prototype.eval = function(env) { - env.error({ - chunk: this.chunk, - index: this.index, - type: 'syntax', - message: this.message || "Invalid code: " + this.chunk - }); - return { - is: 'undefined' - }; -}; -})(require('../tree')); -(function(tree) { - -tree.Keyword = function Keyword(value) { - this.value = value; - var special = { - 'transparent': 'color', - 'true': 'boolean', - 'false': 'boolean' - }; - this.is = special[value] ? special[value] : 'keyword'; -}; -tree.Keyword.prototype = { - eval: function() { return this; }, - toString: function() { return this.value; } -}; - -})(require('../tree')); -(function(tree) { - -tree.Layer = function Layer(obj) { - this.name = obj.name; - this.status = obj.status; - this.styles = obj.styles; - this.properties = obj.properties || {}; - this.srs = obj.srs; - this.datasource = obj.Datasource; -}; - -tree.Layer.prototype.toXML = function() { - var dsoptions = []; - for (var i in this.datasource) { - dsoptions.push(''); - } - - var prop_string = ''; - for (var i in this.properties) { - prop_string += ' ' + i + '="' + this.properties[i] + '"\n'; - } - - return '\n ' + - this.styles.reverse().map(function(s) { - return '' + s + ''; - }).join('\n ') + - '\n \n ' + - dsoptions.join('\n ') + - '\n \n' + - ' \n'; -}; - -})(require('../tree')); -// A literal is a literal string for Mapnik - the -// result of the combination of a `tree.Field` with any -// other type. -(function(tree) { - -tree.Literal = function Field(content) { - this.value = content || ''; - this.is = 'field'; -}; - -tree.Literal.prototype = { - toString: function() { - return this.value; - }, - 'eval': function() { - return this; - } -}; - -})(require('../tree')); -(function(tree) { - -tree.Operation = function Operation(op, operands, index) { - this.op = op.trim(); - this.operands = operands; - this.index = index; - this.is = 'operation'; -}; - -tree.Operation.prototype.eval = function(env) { - var a = this.operands[0].eval(env), - b = this.operands[1].eval(env), - temp; - - if (a.is === 'undefined' || b.is === 'undefined') { - return { - is: 'undefined', - value: 'undefined' - }; - } - - if (a instanceof tree.Dimension && b instanceof tree.Color) { - if (this.op === '*' || this.op === '+') { - temp = b, b = a, a = temp; - } else { - env.error({ - name: "OperationError", - message: "Can't substract or divide a color from a number", - index: this.index - }); - } - } - - // Only concatenate plain strings, because this is easily - // pre-processed - if (a instanceof tree.Quoted && b instanceof tree.Quoted && this.op !== '+') { - env.error({ - message: "Can't subtract, divide, or multiply strings.", - index: this.index, - type: 'runtime', - filename: this.filename - }); - return { - is: 'undefined', - value: 'undefined' - }; - } - - // Fields, literals, dimensions, and quoted strings can be combined. - if (a instanceof tree.Field || b instanceof tree.Field || - a instanceof tree.Literal || b instanceof tree.Literal) { - if (a.is === 'color' || b.is === 'color') { - env.error({ - message: "Can't subtract, divide, or multiply colors in expressions.", - index: this.index, - type: 'runtime', - filename: this.filename - }); - return { - is: 'undefined', - value: 'undefined' - }; - } else { - return new tree.Literal(a.eval(env).toString(true) + this.op + b.eval(env).toString(true)); - } - } - - return a.operate(this.op, b); -}; - -tree.operate = function(op, a, b) { - switch (op) { - case '+': return a + b; - case '-': return a - b; - case '*': return a * b; - case '%': return a % b; - case '/': return a / b; - } -}; - -})(require('../tree')); -(function(tree) { - -tree.Quoted = function Quoted(content) { - this.value = content || ''; - this.is = 'string'; -}; - -tree.Quoted.prototype = { - toString: function(quotes) { - var xmlvalue = this.value.replace(/\'/g, '''); - return (quotes === true) ? "'" + xmlvalue + "'" : this.value; - }, - - 'eval': function() { - return this; - }, - - operate: function(op, other) { - return new tree.Quoted(true, - tree.operate(op, this.toString(), other.toString(this.contains_field))); - } -}; - -})(require('../tree')); -/* - * Carto pulls in a reference from the `mapnik-reference` - * module. This file builds indexes from that file for its various - * options, and provides validation methods for property: value - * combinations. - */ -(function(tree) { - - -var _ = require('underscore'); -var reference = require('mapnik-reference'); - -tree.Reference = { - data: reference.version.latest -}; - -tree.Reference.set = function(ref, version) { - reference = ref; - tree.Reference.setVersion(version || 'latest'); -}; - -tree.Reference.setVersion = function(version) { - tree.Reference.data = reference.version[version]; -}; - -tree.Reference.required_prop_list_cache = {}; - -tree.Reference.selectors = tree.Reference.selectors || (function() { - var list = []; - for (var i in tree.Reference.data.symbolizers) { - for (var j in tree.Reference.data.symbolizers[i]) { - if (tree.Reference.data.symbolizers[i][j].hasOwnProperty('css')) { - list.push(tree.Reference.data.symbolizers[i][j].css); - } - } - } - return list; -})(); - -tree.Reference.validSelector = function(selector) { - return tree.Reference.selectors.indexOf(selector) !== -1; -}; - -tree.Reference.selectorName = function(selector) { - for (var i in tree.Reference.data.symbolizers) { - for (var j in tree.Reference.data.symbolizers[i]) { - if (selector == tree.Reference.data.symbolizers[i][j].css) { - return j; - } - } - } -}; - -tree.Reference.selector = function(selector) { - for (var i in tree.Reference.data.symbolizers) { - for (var j in tree.Reference.data.symbolizers[i]) { - if (selector == tree.Reference.data.symbolizers[i][j].css) { - return tree.Reference.data.symbolizers[i][j]; - } - } - } -}; - -tree.Reference.symbolizer = function(selector) { - for (var i in tree.Reference.data.symbolizers) { - for (var j in tree.Reference.data.symbolizers[i]) { - if (selector == tree.Reference.data.symbolizers[i][j].css) { - return i; - } - } - } -}; - -/* - * For transform properties and image-filters, - * mapnik has its own functions. - */ -tree.Reference.mapnikFunction = function(name) { - var functions = []; - for (var i in tree.Reference.data.symbolizers) { - for (var j in tree.Reference.data.symbolizers[i]) { - if (tree.Reference.data.symbolizers[i][j].type === 'functions') { - functions = functions.concat(tree.Reference.data.symbolizers[i][j].functions); - } - } - } - return _.find(functions, function(f) { - return f[0] === name; - }); -}; - -tree.Reference.requiredPropertyList = function(symbolizer_name) { - if (this.required_prop_list_cache[symbolizer_name]) { - return this.required_prop_list_cache[symbolizer_name]; - } - var properties = []; - for (var j in tree.Reference.data.symbolizers[symbolizer_name]) { - if (tree.Reference.data.symbolizers[symbolizer_name][j].required) { - properties.push(tree.Reference.data.symbolizers[symbolizer_name][j].css); - } - } - return this.required_prop_list_cache[symbolizer_name] = properties; -}; - -tree.Reference.requiredProperties = function(symbolizer_name, rules) { - var req = tree.Reference.requiredPropertyList(symbolizer_name); - for (var i in req) { - if (!(req[i] in rules)) { - return 'Property ' + req[i] + ' required for defining ' + - symbolizer_name + ' styles.'; - } - } -}; - -/** - * TODO: finish implementation - this is dead code - */ -tree.Reference._validateValue = { - 'font': function(env, value) { - if (env.validation_data && env.validation_data.fonts) { - return env.validation_data.fonts.indexOf(value) != -1; - } else { - return true; - } - } -}; - -tree.Reference.isFont = function(selector) { - return tree.Reference.selector(selector).validate == 'font'; -}; - -tree.Reference.validValue = function(env, selector, value) { - var i, j; - // TODO: handle in reusable way - if (!tree.Reference.selector(selector)) { - return false; - } else if (value.value[0].is == 'keyword') { - return tree.Reference - .selector(selector).type - .indexOf(value.value[0].value) !== -1; - } else if (value.value[0].is == 'undefined') { - // caught earlier in the chain - ignore here so that - // error is not overridden - return true; - } else if (tree.Reference.selector(selector).type == 'numbers') { - for (i in value.value) { - if (value.value[i].is !== 'float') { - return false; - } - } - return true; - } else if (tree.Reference.selector(selector).type == 'functions') { - // For backwards compatibility, you can specify a string for `functions`-compatible - // values, though they will not be validated. - if (value.value[0].is === 'string') { - return true; - } else { - for (i in value.value) { - for (j in value.value[i].value) { - if (value.value[i].value[j].is !== 'call') { - return false; - } - var f = _.find(tree.Reference - .selector(selector).functions, function(x) { - return x[0] == value.value[i].value[j].name; - }); - // This filter is unknown - if (!f) return false; - // The filter has been given an incorrect number of arguments - if (f[1] !== value.value[i].value[j].args.length) return false; - } - } - return true; - } - } else if (tree.Reference.selector(selector).type == 'expression') { - return true; - } else if (tree.Reference.selector(selector).type === 'unsigned') { - if (value.value[0].is === 'float') { - value.value[0].round(); - return true; - } else { - return false; - } - } else { - if (tree.Reference.selector(selector).validate) { - var valid = false; - for (i = 0; i < value.value.length; i++) { - if (tree.Reference.selector(selector).type == value.value[i].is && - tree.Reference - ._validateValue - [tree.Reference.selector(selector).validate] - (env, value.value[i].value)) { - return true; - } - } - return valid; - } else { - return tree.Reference.selector(selector).type == value.value[0].is; - } - } -}; - -})(require('../tree')); -(function(tree) { -tree.Rule = function Rule(name, value, index, filename) { - var parts = name.split('/'); - this.name = parts.pop(); - this.instance = parts.length ? parts[0] : '__default__'; - this.value = (value instanceof tree.Value) ? - value : new tree.Value([value]); - this.index = index; - this.symbolizer = tree.Reference.symbolizer(this.name); - this.filename = filename; - this.variable = (name.charAt(0) === '@'); -}; - -tree.Rule.prototype.clone = function() { - var clone = Object.create(tree.Rule.prototype); - clone.name = this.name; - clone.value = this.value; - clone.index = this.index; - clone.instance = this.instance; - clone.symbolizer = this.symbolizer; - clone.filename = this.filename; - clone.variable = this.variable; - return clone; -}; - -tree.Rule.prototype.updateID = function() { - return this.id = this.zoom + '#' + this.name; -}; - -tree.Rule.prototype.toString = function() { - return '[' + tree.Zoom.toString(this.zoom) + '] ' + this.name + ': ' + this.value; -}; - -// second argument, if true, outputs the value of this -// rule without the usual attribute="content" wrapping. Right -// now this is just for the TextSymbolizer, but applies to other -// properties in reference.json which specify serialization=content -tree.Rule.prototype.toXML = function(env, content, sep, format) { - if (!tree.Reference.validSelector(this.name)) { - return env.error({ - message: "Unrecognized rule: " + this.name, - index: this.index, - type: 'syntax', - filename: this.filename - }); - } - - if ((this.value instanceof tree.Value) && - !tree.Reference.validValue(env, this.name, this.value)) { - if (!tree.Reference.selector(this.name)) { - return env.error({ - message: 'Unrecognized property: ' + - this.name, - index: this.index, - type: 'syntax', - filename: this.filename - }); - } else { - return env.error({ - message: 'Invalid value for ' + - this.name + - ', a valid ' + - (tree.Reference.selector(this.name).validate || - tree.Reference.selector(this.name).type) + - ' is expected. ' + this.value + - ' was given.', - index: this.index, - type: 'syntax', - filename: this.filename - }); - } - } - - if (this.variable) { - return ''; - } else if (tree.Reference.isFont(this.name) && this.value.value.length > 1) { - var f = tree._getFontSet(env, this.value.value); - return 'fontset-name="' + f.name + '"'; - } else if (content) { - return this.value.toString(env, this.name, sep); - } else { - return tree.Reference.selectorName(this.name) + - '="' + - this.value.toString(env, this.name) + - '"'; - } -}; - -/** - * TODO: Rule eval chain should add fontsets to env.frames - */ -tree.Rule.prototype['eval'] = function(context) { - return new tree.Rule(this.name, - this.value['eval'](context), - this.index, - this.filename); -}; - -})(require('../tree')); -(function(tree) { - -tree.Ruleset = function Ruleset(selectors, rules) { - this.selectors = selectors; - this.rules = rules; - // static cache of find() function - this._lookups = {}; -}; -tree.Ruleset.prototype = { - eval: function(env) { - var ruleset = new tree.Ruleset(this.selectors, this.rules.slice(0)); - ruleset.root = this.root; - - // push the current ruleset to the frames stack - env.frames.unshift(ruleset); - - // Evaluate imports - if (ruleset.root) { - for (var i = 0; i < ruleset.rules.length; i++) { - if (ruleset.rules[i] instanceof tree.Import) { - Array.prototype.splice - .apply(ruleset.rules, [i, 1].concat(ruleset.rules[i].eval(env))); - } - } - } - - // Evaluate everything else - for (var i = 0, rule; i < ruleset.rules.length; i++) { - rule = ruleset.rules[i]; - ruleset.rules[i] = rule.eval ? rule.eval(env) : rule; - } - - // Pop the stack - env.frames.shift(); - - return ruleset; - }, - match: function(args) { - return !args || args.length === 0; - }, - variables: function() { - if (this._variables) { return this._variables; } - else { - return this._variables = this.rules.reduce(function(hash, r) { - if (r instanceof tree.Rule && r.variable === true) { - hash[r.name] = r; - } - return hash; - }, {}); - } - }, - variable: function(name) { - return this.variables()[name]; - }, - /** - * Extend this rule by adding rules from another ruleset - * - * Currently this is designed to accept less specific - * rules and add their values only if this ruleset doesn't - * contain them. - */ - - rulesets: function() { - if (this._rulesets) { return this._rulesets; } - else { - return this._rulesets = this.rules.filter(function(r) { - return (r instanceof tree.Ruleset); - }); - } - }, - find: function(selector, self) { - self = self || this; - var rules = [], rule, match, - key = selector.toString(); - - if (key in this._lookups) { return this._lookups[key]; } - - this.rulesets().forEach(function(rule) { - if (rule !== self) { - for (var j = 0; j < rule.selectors.length; j++) { - if (match = selector.match(rule.selectors[j])) { - if (selector.elements.length > 1) { - Array.prototype.push.apply(rules, rule.find( - new tree.Selector(null, null, null, selector.elements.slice(1)), self)); - } else { - rules.push(rule); - } - break; - } - } - } - }); - return this._lookups[key] = rules; - }, - flatten: function(result, parents, env) { - var selectors = []; - if (this.selectors.length === 0) { - env.frames = env.frames.concat(this.rules); - } - for (var i = 0; i < this.selectors.length; i++) { - var child = this.selectors[i]; - - // This is an invalid filterset. - if (!child.filters) continue; - - if (parents.length) { - for (var j = 0; j < parents.length; j++) { - var parent = parents[j]; - - var mergedFilters = parent.filters.cloneWith(child.filters); - if (mergedFilters === null) { - // Filters could be added, but they didn't change the - // filters. This means that we only have to clone when - // the zoom levels or the attachment is different too. - if (parent.zoom === (parent.zoom & child.zoom) && - parent.frame_offset === child.frame_offset && - parent.attachment === child.attachment) { - continue; - } else { - mergedFilters = parent.filters; - } - } else if (!mergedFilters) { - // The merged filters are invalid, that means we don't - // have to clone. - continue; - } - - var clone = Object.create(tree.Selector.prototype); - clone.filters = mergedFilters; - clone.zoom = parent.zoom & child.zoom; - clone.frame_offset = child.frame_offset; - clone.elements = parent.elements.concat(child.elements); - if (parent.attachment && child.attachment) { - clone.attachment = parent.attachment + '/' + child.attachment; - } - else clone.attachment = child.attachment || parent.attachment; - clone.conditions = parent.conditions + child.conditions; - clone.index = child.index; - selectors.push(clone); - } - } else { - selectors.push(child); - } - } - - var rules = []; - for (var i = 0; i < this.rules.length; i++) { - var rule = this.rules[i]; - - if (rule instanceof tree.Ruleset) { - rule.flatten(result, selectors, env); - } else if (rule instanceof tree.Rule) { - rules.push(rule); - } else if (rule instanceof tree.Invalid) { - env.error(rule); - } - } - - var index = rules.length ? rules[0].index : false; - for (var i = 0; i < selectors.length; i++) { - // For specificity sort, use the position of the first rule to allow - // defining attachments that are under current element as a descendant - // selector. - if (index !== false) { - selectors[i].index = index; - } - result.push(new tree.Definition(selectors[i], rules.slice())); - } - - return result; - } -}; -})(require('../tree')); -var assert = require('assert'); - -(function(tree) { - -tree.Selector = function Selector(filters, zoom, frame_offset, elements, attachment, conditions, index) { - this.elements = elements || []; - this.attachment = attachment; - this.filters = filters || {}; - this.frame_offset = frame_offset; - this.zoom = typeof zoom !== 'undefined' ? zoom : tree.Zoom.all; - this.conditions = conditions; - this.index = index; -}; - -/** - * Determine the specificity of this selector - * based on the specificity of its elements - calling - * Element.specificity() in order to do so - * - * [ID, Class, Filters, Position in document] - */ -tree.Selector.prototype.specificity = function() { - return this.elements.reduce(function(memo, e) { - var spec = e.specificity(); - memo[0] += spec[0]; - memo[1] += spec[1]; - return memo; - }, [0, 0, this.conditions, this.index]); -}; - -})(require('../tree')); -(function(tree) { -var _ = require('underscore'); - -tree.Style = function Style(name, attachment, definitions) { - this.attachment = attachment; - this.definitions = definitions; - this.name = name + (attachment !== '__default__' ? '-' + attachment : ''); -}; - -tree.Style.prototype.toXML = function(env) { - var existing = {}; - - var image_filters = _.flatten(this.definitions.map(function(definition) { - return definition.rules.filter(function(rule) { - return (rule.name === 'image-filters'); - }); - })); - - var comp_op = _.flatten(this.definitions.map(function(definition) { - return definition.rules.filter(function(rule) { - return (rule.name === 'composite-operation'); - }); - })); - - var opacity = _.flatten(this.definitions.map(function(definition) { - return definition.rules.filter(function(rule) { - return (rule.name === 'opacity'); - }); - })); - - var rules = this.definitions.map(function(definition) { - return definition.toXML(env, existing); - }); - - var attrs_xml = ''; - - if (image_filters.length) { - attrs_xml += ' image-filters="' + image_filters.map(function(f) { - return f.eval(env).toXML(env, true, ' ', 'image-filter'); - }).join(' ') + '" '; - } - - if (comp_op.length) { - attrs_xml += ' comp-op="' + comp_op[0].value.eval(env).toString() + '" '; - } - - if (opacity.length) { - attrs_xml += ' opacity="' + opacity[0].value.eval(env).toString() + '" '; - } - - return ''; -}; - -})(require('../tree')); -(function(tree) { - -tree.URL = function URL(val, paths) { - this.value = val; - this.paths = paths; - this.is = 'uri'; -}; -tree.URL.prototype = { - toString: function() { - return this.value.toString(); - }, - eval: function(ctx) { - return new tree.URL(this.value.eval(ctx), this.paths); - } -}; - -})(require('../tree')); -(function(tree) { - -tree.Value = function Value(value) { - this.value = value; - this.is = 'value'; -}; -tree.Value.prototype = { - eval: function(env) { - if (this.value.length === 1) { - return this.value[0].eval(env); - } else { - return new tree.Value(this.value.map(function(v) { - return v.eval(env); - })); - } - }, - toString: function(env, selector, sep, format) { - return this.value.map(function(e) { - return e.toString(env, format); - }).join(sep || ', '); - }, - clone: function() { - var obj = Object.create(tree.Value.prototype); - if (Array.isArray(obj)) obj.value = this.value.slice(); - else obj.value = this.value; - obj.is = this.is; - return obj; - } -}; - -})(require('../tree')); -(function(tree) { - -tree.Variable = function Variable(name, index, filename) { - this.name = name; - this.index = index; - this.filename = filename; -}; -tree.Variable.prototype = { - eval: function(env) { - var variable, - v, - that = this, - name = this.name; - - if (this._css) return this._css; - - var thisframe = env.frames.filter(function(f) { - return f.name == that.name; - }); - if (thisframe.length) { - return thisframe[0].value.eval(env); - } else { - env.error({ - message: 'variable ' + this.name + ' is undefined', - index: this.index, - type: 'runtime', - filename: this.filename - }); - return { - is: 'undefined', - value: 'undefined' - }; - } - } -}; - -})(require('../tree')); -var tree = require('../tree'); - -// Storage for zoom ranges. Only supports continuous ranges, -// and stores them as bit-sequences so that they can be combined, -// inverted, and compared quickly. -tree.Zoom = function(op, value, index) { - value = parseInt(value, 10); - if (value > tree.Zoom.maxZoom || value < 0) { - throw { - message: 'Only zoom levels between 0 and ' + - tree.Zoom.maxZoom + ' supported.', - index: index - }; - } - - var start = 0, - end = Infinity, - zoom = 0; - - switch (op) { - case '=': - return 1 << value; - case '>': - start = value + 1; - break; - case '>=': - start = value; - break; - case '<': - end = value - 1; - break; - case '<=': - end = value; - break; - } - for (var i = 0; i <= tree.Zoom.maxZoom; i++) { - if (i >= start && i <= end) { - zoom |= (1 << i); - } - } - return zoom; -}; - -// Covers all zoomlevels from 0 to 22 -tree.Zoom.all = 0x7FFFFF; - -tree.Zoom.maxZoom = 22; - -tree.Zoom.ranges = { - 0: 1000000000, - 1: 500000000, - 2: 200000000, - 3: 100000000, - 4: 50000000, - 5: 25000000, - 6: 12500000, - 7: 6500000, - 8: 3000000, - 9: 1500000, - 10: 750000, - 11: 400000, - 12: 200000, - 13: 100000, - 14: 50000, - 15: 25000, - 16: 12500, - 17: 5000, - 18: 2500, - 19: 1500, - 20: 750, - 21: 500, - 22: 250, - 23: 100 -}; - -// Only works for single range zooms. `[XXX....XXXXX.........]` is invalid. -tree.Zoom.toXML = function(zoom) { - var conditions = []; - if (zoom != tree.Zoom.all) { - var start = null, end = null; - for (var i = 0; i <= tree.Zoom.maxZoom; i++) { - if (zoom & (1 << i)) { - if (start === null) start = i; - end = i; - } - } - if (start > 0) conditions.push(' ' + - tree.Zoom.ranges[start] + '\n'); - if (end < 22) conditions.push(' ' + - tree.Zoom.ranges[end + 1] + '\n'); - } - return conditions; -}; - - -tree.Zoom.toString = function(zoom) { - var str = ''; - for (var i = 0; i <= tree.Zoom.maxZoom; i++) { - str += (zoom & (1 << i)) ? 'X' : '.'; - } - return str; -}; -(function (tree) { - -tree.functions = { - rgb: function (r, g, b) { - return this.rgba(r, g, b, 1.0); - }, - rgba: function (r, g, b, a) { - var rgb = [r, g, b].map(function (c) { return number(c); }); - a = number(a); - return new tree.Color(rgb, a); - }, - hsl: function (h, s, l) { - return this.hsla(h, s, l, 1.0); - }, - hsla: function (h, s, l, a) { - h = (number(h) % 360) / 360; - s = number(s); l = number(l); a = number(a); - - var m2 = l <= 0.5 ? l * (s + 1) : l + s - l * s; - var m1 = l * 2 - m2; - - return this.rgba(hue(h + 1/3) * 255, - hue(h) * 255, - hue(h - 1/3) * 255, - a); - - function hue(h) { - h = h < 0 ? h + 1 : (h > 1 ? h - 1 : h); - if (h * 6 < 1) return m1 + (m2 - m1) * h * 6; - else if (h * 2 < 1) return m2; - else if (h * 3 < 2) return m1 + (m2 - m1) * (2/3 - h) * 6; - else return m1; - } - }, - hue: function (color) { - return new tree.Dimension(Math.round(color.toHSL().h)); - }, - saturation: function (color) { - return new tree.Dimension(Math.round(color.toHSL().s * 100), '%'); - }, - lightness: function (color) { - return new tree.Dimension(Math.round(color.toHSL().l * 100), '%'); - }, - alpha: function (color) { - return new tree.Dimension(color.toHSL().a); - }, - saturate: function (color, amount) { - var hsl = color.toHSL(); - - hsl.s += amount.value / 100; - hsl.s = clamp(hsl.s); - return hsla(hsl); - }, - desaturate: function (color, amount) { - var hsl = color.toHSL(); - - hsl.s -= amount.value / 100; - hsl.s = clamp(hsl.s); - return hsla(hsl); - }, - lighten: function (color, amount) { - var hsl = color.toHSL(); - - hsl.l += amount.value / 100; - hsl.l = clamp(hsl.l); - return hsla(hsl); - }, - darken: function (color, amount) { - var hsl = color.toHSL(); - - hsl.l -= amount.value / 100; - hsl.l = clamp(hsl.l); - return hsla(hsl); - }, - fadein: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a += amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - fadeout: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a -= amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - spin: function (color, amount) { - var hsl = color.toHSL(); - var hue = (hsl.h + amount.value) % 360; - - hsl.h = hue < 0 ? 360 + hue : hue; - - return hsla(hsl); - }, - replace: function (entity, a, b) { - if (entity.is === 'field') { - return entity.toString + '.replace(' + a.toString() + ', ' + b.toString() + ')'; - } else { - return entity.replace(a, b); - } - }, - // - // Copyright (c) 2006-2009 Hampton Catlin, Nathan Weizenbaum, and Chris Eppstein - // http://sass-lang.com - // - mix: function (color1, color2, weight) { - var p = weight.value / 100.0; - var w = p * 2 - 1; - var a = color1.toHSL().a - color2.toHSL().a; - - var w1 = (((w * a == -1) ? w : (w + a) / (1 + w * a)) + 1) / 2.0; - var w2 = 1 - w1; - - var rgb = [color1.rgb[0] * w1 + color2.rgb[0] * w2, - color1.rgb[1] * w1 + color2.rgb[1] * w2, - color1.rgb[2] * w1 + color2.rgb[2] * w2]; - - var alpha = color1.alpha * p + color2.alpha * (1 - p); - - return new tree.Color(rgb, alpha); - }, - greyscale: function (color) { - return this.desaturate(color, new tree.Dimension(100)); - }, - '%': function (quoted /* arg, arg, ...*/) { - var args = Array.prototype.slice.call(arguments, 1), - str = quoted.value; - - for (var i = 0; i < args.length; i++) { - str = str.replace(/%s/, args[i].value) - .replace(/%[da]/, args[i].toString()); - } - str = str.replace(/%%/g, '%'); - return new tree.Quoted(str); - } -}; - -var image_filter_functors = [ - 'emboss', 'blur', 'gray', 'sobel', 'edge-detect', - 'x-gradient', 'y-gradient', 'sharpen']; - -for (var i = 0; i < image_filter_functors.length; i++) { - var f = image_filter_functors[i]; - tree.functions[f] = (function(f) { - return function() { - return new tree.ImageFilter(f); - }; - })(f); -} - -tree.functions['agg-stack-blur'] = function(x, y) { - return new tree.ImageFilter('agg-stack-blur', [x, y]); -}; - -function hsla(hsla) { - return tree.functions.hsla(hsla.h, hsla.s, hsla.l, hsla.a); -} - -function number(n) { - if (n instanceof tree.Dimension) { - return parseFloat(n.unit == '%' ? n.value / 100 : n.value); - } else if (typeof(n) === 'number') { - return n; - } else { - throw new Error('Color functions take numbers as parameters.'); - } -} - -function clamp(val) { - return Math.min(1, Math.max(0, val)); -} - -})(require('./tree')); -(function(carto) { -var tree = require('./tree'); -var _ = require('underscore'); - -// monkey patch less classes -tree.Value.prototype.toJS = function() { - //var v = this.value[0].value[0]; - var val = this.eval() - var v = val.toString(); - if(val.is === "color" || val.is === 'uri' || val.is === 'string' || val.is === 'keyword') { - v = "'" + v + "'"; - } else if (val.is === 'field') { - // replace [varuable] by ctx['variable'] - v = v.replace(/\[(.*)\]/g, "data['\$1']") - } - return "_value = " + v + ";"; -}; - -Object.defineProperty(tree.Filterset.prototype, 'toJS', { - enumerable: false, - value: function(env) { - var opMap = { - '=': '===' - }; - return _.map(this, function(filter) { - var op = filter.op; - if(op in opMap) { - op = opMap[op]; - } - var val = filter.val; - if(filter._val !== undefined) { - val = filter._val.toString(true); - } - - var attrs = "data"; - return attrs + "." + filter.key + " " + op + " " + val; - }).join(' && '); - } -}); - -tree.Definition.prototype.toJS = function() { - var shaderAttrs = {}; - - // merge conditions from filters with zoom condition of the - // definition - var zoom = "(" + this.zoom + " & (1 << ctx.zoom))"; - var frame_offset = this.frame_offset; - var _if = this.filters.toJS(); - var filters = [zoom]; - if(_if) filters.push(_if); - if(frame_offset) filters.push('ctx["frame-offset"] === ' + frame_offset); - _if = filters.join(" && "); - _.each(this.rules, function(rule) { - if(rule instanceof tree.Rule) { - shaderAttrs[rule.name] = shaderAttrs[rule.name] || []; - if (_if) { - shaderAttrs[rule.name].push( - "if(" + _if + "){" + rule.value.toJS() + "}" - ); - } else { - shaderAttrs[rule.name].push(rule.value.toJS()); - } - } else { - if (rule instanceof tree.Ruleset) { - var sh = rule.toJS(); - for(var v in sh) { - shaderAttrs[v] = shaderAttrs[v] || []; - for(var attr in sh[v]) { - shaderAttrs[v].push(sh[v][attr]); - } - } - } - } - }); - return shaderAttrs; -}; - - -function CartoCSS(style, options) { - this.options = options || {}; - if(style) { - this.setStyle(style); - } -} - -CartoCSS.Layer = function(shader, options) { - this.options = options; - this.shader = shader; -}; - -CartoCSS.renderers = {}; - -CartoCSS.renderers['svg'] = { - - maps: {}, - - transform: function(src) { - var target = {}; - for(var i in src) { - var t = this.maps[i]; - if(t) { - target[t] = src[i]; - } else { - // copy it - target[i] = src[i]; - } - } - return target; - } - -}; - -CartoCSS.renderers['canvas-2d'] = { - - maps: { - // marker - 'marker-width': 'point-radius', - 'marker-line-color': 'strokeStyle', - 'marker-line-width': 'lineWidth', - 'marker-line-opacity': 'strokeOpacity', - 'marker-fill-opacity': 'fillOpacity', - 'marker-opacity': 'fillOpacity', - 'marker-fill': 'fillStyle', - 'marker-file': function(attr) { - var img = new Image(); - img.src = attr; - return img; - }, - // point - 'point-color': 'fillStyle', - 'point-file': function(attr) { - var img = new Image(); - img.src = attr; - return img; - }, - // line - 'line-color': 'strokeStyle', - 'line-width': 'lineWidth', - 'line-opacity': 'globalAlpha', - // polygon - 'polygon-fill': 'fillStyle', - 'polygon-opacity': 'globalAlpha', - 'comp-op': 'comp-op' - }, - - transform: function(src) { - var target = {}; - for(var i in src) { - var t = this.maps[i]; - if(t) { - if(typeof(t) === 'function') { - target[i] = t(src[i]); - } else { - target[t] = src[i]; - } - } else { - // copy it - target[i] = src[i]; - } - } - return target; - } -} - -var renderer = CartoCSS.renderers['svg']; -var ref = require('mapnik-reference').version.latest; -var to_load = ['polygon', 'line', 'point', 'markers']; -for(var ss in to_load) { - var s = to_load[ss]; - for(var i in ref.symbolizers[s]) { - renderer.maps[ref.symbolizers[s][i].css] = i; - } -} - - -CartoCSS.Layer.prototype = { - - fullName: function() { - return this.shader.attachment; - }, - - name: function() { - return this.fullName().split('::')[0]; - }, - - // frames this layer need to be rendered - frames: function() { - return this.shader.frames; - }, - - attachment: function() { - return this.fullName().split('::')[1]; - }, - - eval: function(prop) { - var p = this.shader[prop]; - if (!p) return; - return p({}, { zoom: 0, 'frame-offset': 0 }); - }, - - /* - * `target`: style, 'svg', 'canvas-2d'... - * `props`: feature properties - * `context`: rendering properties, i.e zoom - */ - getStyle: function(target, props, context) { - var style = {}; - for(var i in this.shader) { - if(i !== 'attachment' && i !== 'zoom' && i !== 'frames') { - style[i] = this.shader[i](props, context); - } - } - return CartoCSS.renderers[target].transform(style); - }, - - /** - * returns true if a feature needs to be rendered - */ - filter: function(featureType, props, context) { - for(var i in this.shader) { - var s = this.shader[i](props, context); - if(s) { - return true; - } - } - return false; - }, - - // - // given a geoemtry type returns the transformed one acording the CartoCSS - // For points there are two kind of types: point and sprite, the first one - // is a circle, second one is an image sprite - // - // the other geometry types are the same than geojson (polygon, linestring...) - // - transformGeometry: function(type) { - return type; - }, - - transformGeometries: function(geojson) { - return geojson; - } - -}; - -CartoCSS.prototype = { - - setStyle: function(style) { - var layers = this.parse(style); - if(!layers) { - throw new Error(this.parse_env.errors); - } - this.layers = layers.map(function(shader) { - return new CartoCSS.Layer(shader); - }); - }, - - getLayers: function() { - return this.layers; - }, - - getDefault: function() { - return this.findLayer({ attachment: '__default__' }); - }, - - findLayer: function(where) { - return _.find(this.layers, function(value) { - for (var key in where) { - var v = value[key]; - if (typeof(v) === 'function') { - v = v.call(value); - } - if (where[key] !== v) return false; - } - return true; - }); - }, - - _createFn: function(ops) { - var body = ops.join('\n'); - if(this.options.debug) console.log(body); - return Function("data","ctx", "var _value = null; " + body + "; return _value; "); - }, - - _compile: function(shader) { - if(typeof shader === 'string') { - shader = eval("(function() { return " + shader +"; })()"); - } - this.shader_src = shader; - for(var attr in shader) { - var c = mapper[attr]; - if(c) { - this.compiled[c] = eval("(function() { return shader[attr]; })();"); - } - } - }, - - parse: function(cartocss) { - var parse_env = { - frames: [], - errors: [], - error: function(obj) { - this.errors.push(obj); - } - }; - this.parse_env = parse_env; - - var ruleset = null; - try { - ruleset = (new carto.Parser(parse_env)).parse(cartocss); - } catch(e) { - console.log(e.stack); - // add the style.mss string to match the response from the server - parse_env.errors.push(e.message); - return; - } - if(ruleset) { - var defs = ruleset.toList(parse_env); - defs.reverse(); - // group by elements[0].value::attachment - var layers = {}; - for(var i = 0; i < defs.length; ++i) { - var def = defs[i]; - var key = def.elements[0] + "::" + def.attachment; - var layer = layers[key] = (layers[key] || {}); - layer.frames = []; - layer.zoom = tree.Zoom.all; - var props = def.toJS(); - for(var v in props) { - (layer[v] = (layer[v] || [])).push(props[v].join('\n')) - } - } - - var ordered_layers = []; - - var done = {}; - for(var i = 0; i < defs.length; ++i) { - var def = defs[i]; - var k = def.elements[0] + "::" + def.attachment; - var layer = layers[k]; - if(!done[k]) { - for(var prop in layer) { - if (prop !== 'zoom' && prop !== 'frames') { - - if(this.options.debug) console.log("****", prop); - layer[prop] = this._createFn(layer[prop]); - } - } - layer.attachment = k; - ordered_layers.push(layer); - done[k] = true; - } - layer.zoom |= def.zoom; - layer.frames.push(def.frame_offset); - } - - // uniq the frames - for(i = 0; i < ordered_layers.length; ++i) { - ordered_layers[i].frames = _.uniq(ordered_layers[i].frames); - } - - return ordered_layers; - - } - return null; - } -}; - - -carto.RendererJS = function (options) { - this.options = options || {}; - this.options.mapnik_version = this.options.mapnik_version || 'latest'; -}; - -// Prepare a javascript object which contains the layers -carto.RendererJS.prototype.render = function render(cartocss, callback) { - tree.Reference.setVersion(this.options.mapnik_version); - return new CartoCSS(cartocss, this.options); -} -if(typeof(module) !== 'undefined') { - module.exports = carto.RendererJS; -} - - -})(require('../carto')); -(function(exports) { - - exports.torque = exports.torque || {}; - - var requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || window.msRequestAnimationFrame || function(c) { setTimeout(c, 16); } - - var cancelAnimationFrame = window.requestAnimationFrame || window.mozCancelAnimationFrame || window.webkitRequestAnimationFrame || window.msRequestAnimationFrame || function(c) { }; - - /** - * options: - * animationDuration in seconds - * animationDelay in seconds - */ - function Animator(callback, options) { - if(!options.steps) { - throw new Error("steps option missing") - } - this.options = options; - this.running = false; - this._tick = this._tick.bind(this); - this._t0 = +new Date(); - this.callback = callback; - this._time = 0.0; - _.defaults(this.options, { - animationDelay: 0, - maxDelta: 0.2, - loop: true - }); - - this.rescale(); - - } - - - Animator.prototype = { - - start: function() { - this.running = true; - requestAnimationFrame(this._tick); - }, - - isRunning: function() { - return this.running; - }, - - stop: function() { - this.pause(); - this.time(0); - }, - - // real animation time - time: function(_) { - if (!arguments.length) return this._time; - this._time = _; - var t = this.range(this.domain(this._time)); - this.callback(t); - }, - - toggle: function() { - if (this.running) { - this.pause() - } else { - this.start() - } - }, - - rescale: function() { - this.domainInv = torque.math.linear(this.options.animationDelay, this.options.animationDelay + this.options.animationDuration); - this.domain = this.domainInv.invert(); - this.range = torque.math.linear(0, this.options.steps); - this.rangeInv = this.range.invert(); - this.time(this._time); - return this; - }, - - duration: function(_) { - if (!arguments.length) return this.options.animationDuration; - this.options.animationDuration = _; - if (this.time() > _) { - this.time(0); - } - this.rescale(); - return this; - }, - - steps: function(_) { - this.options.steps = _; - return this.rescale(); - }, - - step: function(s) { - if(arguments.length === 0) return this.range(this.domain(this._time)); - this._time = this.domainInv(this.rangeInv(s)); - }, - - pause: function() { - this.running = false; - cancelAnimationFrame(this._tick); - }, - - _tick: function() { - var t1 = +new Date(); - var delta = (t1 - this._t0)*0.001; - // if delta is really big means the tab lost the focus - // at some point, so limit delta change - delta = Math.min(this.options.maxDelta, delta); - this._t0 = t1; - this._time += delta; - this.time(this._time); - if(this.step() >= this.options.steps) { - this._time = 0; - } - if(this.running) { - requestAnimationFrame(this._tick); - } - } - - }; - - exports.torque.Animator = Animator; - - - -})(typeof exports === "undefined" ? this : exports); -(function(exports) { - -exports.torque = exports.torque || {}; - -var _torque_reference_latest = { - "version": "1.0.0", - "style": { - "comp-op": { - "css": "comp-op", - "default-value": "src-over", - "default-meaning": "add the current layer on top of other layers", - "doc": "Composite operation. This defines how this layer should behave relative to layers atop or below it.", - "type": [ - "src", // - "src-over", // - "dst-over", // - "src-in", // - "dst-in", // - "src-out", // - "dst-out", // - "src-atop", // - "dst-atop", // - "xor", // - "darken", // - "lighten" // - ] - } - }, - "layer" : { - "buffer-size": { - "default-value": "0", - "type":"float", - "default-meaning": "No buffer will be used", - "doc": "Extra tolerance around the Layer extent (in pixels) used to when querying and (potentially) clipping the layer data during rendering" - }, - "-torque-frame-count": { - "css": "-torque-frame-count", - "default-value": "128", - "type":"number", - "default-meaning": "the data is broken into 128 time frames", - "doc": "Number of animation steps/frames used in the animation. If the data contains a fewere number of total frames, the lesser value will be used." - }, - "-torque-resolution": { - "css": "-torque-resolution", - "default-value": "2", - "type":"number", - "default-meaning": "", - "doc": "Spatial resolution in pixels. A resolution of 1 means no spatial aggregation of the data. Any other resolution of N results in spatial aggregation into cells of NxN pixels. The value N must be power of 2" - }, - "-torque-animation-duration": { - "css": "-torque-animation-duration", - "default-value": "30", - "type":"number", - "default-meaning": "the animation lasts 30 seconds", - "doc": "Animation duration in seconds" - }, - "-torque-aggregation-function": { - "css": "-torque-aggregation-function", - "default-value": "count(cartodb_id)", - "type": "string", - "default-meaning": "the value for each cell is the count of points in that cell", - "doc": "A function used to calculate a value from the aggregate data for each cell. See -torque-resolution" - }, - "-torque-time-attribute": { - "css": "-torque-time-attribute", - "default-value": "time", - "type": "string", - "default-meaning": "the data column in your table that is of a time based type", - "doc": "The table column that contains the time information used create the animation" - }, - "-torque-data-aggregation": { - "css": "-torque-data-aggregation", - "default-value": "linear", - "type": [ - "cumulative" - ], - "default-meaning": "previous values are discarded", - "doc": "A linear animation will discard previous values while a cumulative animation will accumulate them until it restarts" - } - }, - "symbolizers" : { - "*": { - "comp-op": { - "css": "comp-op", - "default-value": "src-over", - "default-meaning": "add the current layer on top of other layers", - "doc": "Composite operation. This defines how this layer should behave relative to layers atop or below it.", - "type": [ - "src", // - "src-over", // - "dst-over", // - "src-in", // - "dst-in", // - "src-out", // - "dst-out", // - "src-atop", // - "dst-atop", // - "xor", // - "darken", // - "lighten" // - ] - }, - "opacity": { - "css": "opacity", - "type": "float", - "doc": "An alpha value for the style (which means an alpha applied to all features in separate buffer and then composited back to main buffer)", - "default-value": 1, - "default-meaning": "no separate buffer will be used and no alpha will be applied to the style after rendering" - } - }, - "trail": { - "steps": { - "css": "trail-steps", - "type": "float", - "default-value": 1, - "default-meaning": "no trail steps", - "doc": "How many steps of trails are going to be rendered" - } - }, - "polygon": { - "fill": { - "css": "polygon-fill", - "type": "color", - "default-value": "rgba(128,128,128,1)", - "default-meaning": "gray and fully opaque (alpha = 1), same as rgb(128,128,128)", - "doc": "Fill color to assign to a polygon" - }, - "fill-opacity": { - "css": "polygon-opacity", - "type": "float", - "doc": "The opacity of the polygon", - "default-value": 1, - "default-meaning": "opaque" - } - }, - "line": { - "stroke": { - "css": "line-color", - "default-value": "rgba(0,0,0,1)", - "type": "color", - "default-meaning": "black and fully opaque (alpha = 1), same as rgb(0,0,0)", - "doc": "The color of a drawn line" - }, - "stroke-width": { - "css": "line-width", - "default-value": 1, - "type": "float", - "doc": "The width of a line in pixels" - }, - "stroke-opacity": { - "css": "line-opacity", - "default-value": 1, - "type": "float", - "default-meaning": "opaque", - "doc": "The opacity of a line" - }, - "stroke-linejoin": { - "css": "line-join", - "default-value": "miter", - "type": [ - "miter", - "round", - "bevel" - ], - "doc": "The behavior of lines when joining" - }, - "stroke-linecap": { - "css": "line-cap", - "default-value": "butt", - "type": [ - "butt", - "round", - "square" - ], - "doc": "The display of line endings" - } - }, - "markers": { - "file": { - "css": "marker-file", - "doc": "An SVG file that this marker shows at each placement. If no file is given, the marker will show an ellipse.", - "default-value": "", - "default-meaning": "An ellipse or circle, if width equals height", - "type": "uri" - }, - "opacity": { - "css": "marker-opacity", - "doc": "The overall opacity of the marker, if set, overrides both the opacity of both the fill and stroke", - "default-value": 1, - "default-meaning": "The stroke-opacity and fill-opacity will be used", - "type": "float" - }, - "fill-opacity": { - "css": "marker-fill-opacity", - "doc": "The fill opacity of the marker", - "default-value": 1, - "default-meaning": "opaque", - "type": "float" - }, - "stroke": { - "css": "marker-line-color", - "doc": "The color of the stroke around a marker shape.", - "default-value": "black", - "type": "color" - }, - "stroke-width": { - "css": "marker-line-width", - "doc": "The width of the stroke around a marker shape, in pixels. This is positioned on the boundary, so high values can cover the area itself.", - "type": "float" - }, - "stroke-opacity": { - "css": "marker-line-opacity", - "default-value": 1, - "default-meaning": "opaque", - "doc": "The opacity of a line", - "type": "float" - }, - "fill": { - "css": "marker-fill", - "default-value": "blue", - "doc": "The color of the area of the marker.", - "type": "color" - }, - "marker-type": { - "css": "marker-type", - "type": [ - "rectangle", - "ellipse" - ], - "default-value": "ellipse", - "doc": "The default marker-type. If a SVG file is not given as the marker-file parameter, the renderer provides either an rectangle or an ellipse (a circle if height is equal to width)" - } - }, - "point": { - "file": { - "css": "point-file", - "type": "uri", - "required": false, - "default-value": "none", - "doc": "Image file to represent a point" - }, - "opacity": { - "css": "point-opacity", - "type": "float", - "default-value": 1.0, - "default-meaning": "Fully opaque", - "doc": "A value from 0 to 1 to control the opacity of the point" - } - } - }, - "colors": { - "aliceblue": [240, 248, 255], - "antiquewhite": [250, 235, 215], - "aqua": [0, 255, 255], - "aquamarine": [127, 255, 212], - "azure": [240, 255, 255], - "beige": [245, 245, 220], - "bisque": [255, 228, 196], - "black": [0, 0, 0], - "blanchedalmond": [255,235,205], - "blue": [0, 0, 255], - "blueviolet": [138, 43, 226], - "brown": [165, 42, 42], - "burlywood": [222, 184, 135], - "cadetblue": [95, 158, 160], - "chartreuse": [127, 255, 0], - "chocolate": [210, 105, 30], - "coral": [255, 127, 80], - "cornflowerblue": [100, 149, 237], - "cornsilk": [255, 248, 220], - "crimson": [220, 20, 60], - "cyan": [0, 255, 255], - "darkblue": [0, 0, 139], - "darkcyan": [0, 139, 139], - "darkgoldenrod": [184, 134, 11], - "darkgray": [169, 169, 169], - "darkgreen": [0, 100, 0], - "darkgrey": [169, 169, 169], - "darkkhaki": [189, 183, 107], - "darkmagenta": [139, 0, 139], - "darkolivegreen": [85, 107, 47], - "darkorange": [255, 140, 0], - "darkorchid": [153, 50, 204], - "darkred": [139, 0, 0], - "darksalmon": [233, 150, 122], - "darkseagreen": [143, 188, 143], - "darkslateblue": [72, 61, 139], - "darkslategrey": [47, 79, 79], - "darkturquoise": [0, 206, 209], - "darkviolet": [148, 0, 211], - "deeppink": [255, 20, 147], - "deepskyblue": [0, 191, 255], - "dimgray": [105, 105, 105], - "dimgrey": [105, 105, 105], - "dodgerblue": [30, 144, 255], - "firebrick": [178, 34, 34], - "floralwhite": [255, 250, 240], - "forestgreen": [34, 139, 34], - "fuchsia": [255, 0, 255], - "gainsboro": [220, 220, 220], - "ghostwhite": [248, 248, 255], - "gold": [255, 215, 0], - "goldenrod": [218, 165, 32], - "gray": [128, 128, 128], - "grey": [128, 128, 128], - "green": [0, 128, 0], - "greenyellow": [173, 255, 47], - "honeydew": [240, 255, 240], - "hotpink": [255, 105, 180], - "indianred": [205, 92, 92], - "indigo": [75, 0, 130], - "ivory": [255, 255, 240], - "khaki": [240, 230, 140], - "lavender": [230, 230, 250], - "lavenderblush": [255, 240, 245], - "lawngreen": [124, 252, 0], - "lemonchiffon": [255, 250, 205], - "lightblue": [173, 216, 230], - "lightcoral": [240, 128, 128], - "lightcyan": [224, 255, 255], - "lightgoldenrodyellow": [250, 250, 210], - "lightgray": [211, 211, 211], - "lightgreen": [144, 238, 144], - "lightgrey": [211, 211, 211], - "lightpink": [255, 182, 193], - "lightsalmon": [255, 160, 122], - "lightseagreen": [32, 178, 170], - "lightskyblue": [135, 206, 250], - "lightslategray": [119, 136, 153], - "lightslategrey": [119, 136, 153], - "lightsteelblue": [176, 196, 222], - "lightyellow": [255, 255, 224], - "lime": [0, 255, 0], - "limegreen": [50, 205, 50], - "linen": [250, 240, 230], - "magenta": [255, 0, 255], - "maroon": [128, 0, 0], - "mediumaquamarine": [102, 205, 170], - "mediumblue": [0, 0, 205], - "mediumorchid": [186, 85, 211], - "mediumpurple": [147, 112, 219], - "mediumseagreen": [60, 179, 113], - "mediumslateblue": [123, 104, 238], - "mediumspringgreen": [0, 250, 154], - "mediumturquoise": [72, 209, 204], - "mediumvioletred": [199, 21, 133], - "midnightblue": [25, 25, 112], - "mintcream": [245, 255, 250], - "mistyrose": [255, 228, 225], - "moccasin": [255, 228, 181], - "navajowhite": [255, 222, 173], - "navy": [0, 0, 128], - "oldlace": [253, 245, 230], - "olive": [128, 128, 0], - "olivedrab": [107, 142, 35], - "orange": [255, 165, 0], - "orangered": [255, 69, 0], - "orchid": [218, 112, 214], - "palegoldenrod": [238, 232, 170], - "palegreen": [152, 251, 152], - "paleturquoise": [175, 238, 238], - "palevioletred": [219, 112, 147], - "papayawhip": [255, 239, 213], - "peachpuff": [255, 218, 185], - "peru": [205, 133, 63], - "pink": [255, 192, 203], - "plum": [221, 160, 221], - "powderblue": [176, 224, 230], - "purple": [128, 0, 128], - "red": [255, 0, 0], - "rosybrown": [188, 143, 143], - "royalblue": [65, 105, 225], - "saddlebrown": [139, 69, 19], - "salmon": [250, 128, 114], - "sandybrown": [244, 164, 96], - "seagreen": [46, 139, 87], - "seashell": [255, 245, 238], - "sienna": [160, 82, 45], - "silver": [192, 192, 192], - "skyblue": [135, 206, 235], - "slateblue": [106, 90, 205], - "slategray": [112, 128, 144], - "slategrey": [112, 128, 144], - "snow": [255, 250, 250], - "springgreen": [0, 255, 127], - "steelblue": [70, 130, 180], - "tan": [210, 180, 140], - "teal": [0, 128, 128], - "thistle": [216, 191, 216], - "tomato": [255, 99, 71], - "turquoise": [64, 224, 208], - "violet": [238, 130, 238], - "wheat": [245, 222, 179], - "white": [255, 255, 255], - "whitesmoke": [245, 245, 245], - "yellow": [255, 255, 0], - "yellowgreen": [154, 205, 50], - "transparent": [0, 0, 0, 0] - } -}; - -exports.torque['torque-reference'] = { - version: { - latest: _torque_reference_latest, - '1.0.0': _torque_reference_latest - } -} - -})(typeof exports === "undefined" ? this : exports); -// -// common functionallity for torque layers -// - -(function(exports) { - -function TorqueLayer() {} - -TorqueLayer.prototype = { -}; - -TorqueLayer.optionsFromLayer = function(mapConfig) { - var opts = {}; - if (!mapConfig) return opts; - var attrs = { - 'buffer-size': 'buffer-size', - '-torque-frame-count': 'steps', - '-torque-resolution': 'resolution', - '-torque-animation-duration': 'animationDuration', - '-torque-aggregation-function': 'countby', - '-torque-time-attribute': 'column', - '-torque-data-aggregation': 'data_aggregation' - }; - for (var i in attrs) { - var v = mapConfig.eval(i); - if (v !== undefined) { - var a = attrs[i]; - opts[a] = v; - } - } - return opts; -}; - -TorqueLayer.optionsFromCartoCSS = function(cartocss) { - var shader = new carto.RendererJS().render(cartocss); - var mapConfig = shader.findLayer({ name: 'Map' }); - return TorqueLayer.optionsFromLayer(mapConfig); -}; - -exports.torque.common = torque.common || {}; -exports.torque.common.TorqueLayer = TorqueLayer; - -})(typeof exports === "undefined" ? this : exports); -(function(exports) { - - exports.torque = exports.torque || {}; - - var Event = {}; - Event.on = function(evt, callback) { - var cb = this._evt_callbacks = this._evt_callbacks || {}; - var l = cb[evt] || (cb[evt] = []); - l.push(callback); - return this; - }; - - Event.trigger = function(evt) { - var c = this._evt_callbacks && this._evt_callbacks[evt]; - for(var i = 0; c && i < c.length; ++i) { - c[i].apply(this, Array.prototype.slice.call(arguments, 1)); - } - return this; - }; - - Event.fire = Event.trigger; - - Event.off = function (evt, callback) { - var c = this._evt_callbacks && this._evt_callbacks[evt]; - if (c && !callback) { - delete this._evt_callbacks[evt]; - return this; - } - var remove = []; - for(var i = 0; c && i < c.length; ++i) { - if(c[i] === callback) remove.push(i); - } - while((i = remove.pop()) !== undefined) c.splice(i, 1); - return this; - }; - - Event.callbacks = function(evt) { - return (this._evt_callbacks && this._evt_callbacks[evt]) || []; - }; - - exports.torque.Event = Event; - exports.torque.extend = function(a, b) { - for (var k in b) { - a[k] = b[k]; - } - return a - } - - exports.torque.clone = function(a) { - return exports.torque.extend({}, a); - } - - exports.torque.isFunction = function(f) { - return typeof f == 'function' || false; - } - - exports.torque.isArray = function(value) { - return value && typeof value == 'object' && Object.prototype.toString.call(value) == '[object Array]'; - }; - - // types - exports.torque.types = { - Uint8Array: typeof(window['Uint8Array']) !== 'undefined' ? window.Uint8Array : Array, - Uint32Array: typeof(window['Uint32Array']) !== 'undefined' ? window.Uint32Array : Array, - Int32Array: typeof(window['Int32Array']) !== 'undefined' ? window.Int32Array: Array - }; - - exports.torque.isBrowserSupported = function() { - return !!document.createElement('canvas'); - }; - - exports.torque.flags = { - sprites_to_images: navigator.userAgent.indexOf('Safari') === -1 - } - -})(typeof exports === "undefined" ? this : exports); - -if (typeof module !== "undefined") { - module.exports = { - cartocss_reference: require('./cartocss_reference').torque['torque-reference'] - }; -} -(function(exports) { - - exports.torque = exports.torque || {}; - - function clamp(a, b) { - return function(t) { - return Math.max(Math.min(t, b), a); - }; - } - - function invLinear(a, b) { - var c = clamp(0, 1.0); - return function(t) { - return c((t - a)/(b - a)); - }; - } - - function linear(a, b) { - var c = clamp(a, b); - function _linear(t) { - return c(a*(1.0 - t) + t*b); - } - - _linear.invert = function() { - return invLinear(a, b); - }; - - return _linear; - } - - exports.torque.math = { - clamp: clamp, - linear: linear, - invLinear: invLinear - }; - -})(typeof exports === "undefined" ? this : exports); - - -(function(exports) { - -var Point = function(x, y) { - this.x = x || 0; - this.y = y || 0; -}; - -function clamp(value, optMin, optMax) { - if (optMin !== null) value = Math.max(value, optMin); - if (optMax !== null) value = Math.min(value, optMax); - return value; -} - -function degreesToRadians(deg) { - return deg * (Math.PI / 180); -} - -function radiansToDegrees(rad) { - return rad / (Math.PI / 180); -} - - -var MercatorProjection = exports.torque.Mercator = function() { -// this._tileSize = L.Browser.retina ? 512 : 256; - this._tileSize = 256; - this._pixelOrigin = new Point(this._tileSize / 2, this._tileSize / 2); - this._pixelsPerLonDegree = this._tileSize / 360; - this._pixelsPerLonRadian = this._tileSize / (2 * Math.PI); -}; - -MercatorProjection.prototype._fromLatLonToPoint = function(lat, lon) { - var point = new Point(0, 0); - var origin = this._pixelOrigin; - - point.x = origin.x + lon * this._pixelsPerLonDegree; - - // NOTE(appleton): Truncating to 0.9999 effectively limits latitude to - // 89.189. This is about a third of a tile past the edge of the world - // tile. - var siny = clamp(Math.sin(degreesToRadians(lat)), -0.9999, 0.9999); - point.y = origin.y + 0.5 * Math.log((1 + siny) / (1 - siny)) * -this._pixelsPerLonRadian; - return point; -}; - -MercatorProjection.prototype._fromPointToLatLon = function(point) { - var me = this; - var origin = me._pixelOrigin; - var lon = (point.x - origin.x) / me._pixelsPerLonDegree; - var latRadians = (point.y - origin.y) / -me._pixelsPerLonRadian; - var lat = radiansToDegrees(2 * Math.atan(Math.exp(latRadians)) - Math.PI / 2); - return { lat:lat, lon:lon }; -}; - -MercatorProjection.prototype._tilePixelPos = function(tileX, tileY) { - return { - x: tileX*this._tileSize, - y: tileY*this._tileSize - }; -}; - -MercatorProjection.prototype.tilePixelBBox = function(x, y, zoom, px, py, res) { - res = res || 1.0; - var numTiles = 1 < 1) { - Profiler.new_value(this.name, this.count); - this.count = 0; - this.start(); - } - } -}; - -Profiler.metric = function(name) { - return new Metric(name); -}; - -exports.Profiler = Profiler; - -})(typeof exports === "undefined" ? this : exports); -(function(exports) { - - var torque = exports.torque = exports.torque || {}; - var providers = exports.torque.providers = exports.torque.providers || {}; - - var Uint8Array = torque.types.Uint8Array; - var Int32Array = torque.types.Int32Array; - var Uint32Array = torque.types.Uint32Array; - - // format('hello, {0}', 'rambo') -> "hello, rambo" - function format(str) { - for(var i = 1; i < arguments.length; ++i) { - var attrs = arguments[i]; - for(var attr in attrs) { - str = str.replace(RegExp('\\{' + attr + '\\}', 'g'), attrs[attr]); - } - } - return str; - } - - var json = function (options) { - this._ready = false; - this._tileQueue = []; - this.options = options; - - this.options.is_time = this.options.is_time === undefined ? true: this.options.is_time; - this.options.tiler_protocol = options.tiler_protocol || 'http'; - this.options.tiler_domain = options.tiler_domain || 'cartodb.com'; - this.options.tiler_port = options.tiler_port || 80; - - if (this.options.data_aggregation) { - this.options.cumulative = this.options.data_aggregation === 'cumulative'; - } - - // check options - if (options.resolution === undefined ) throw new Error("resolution should be provided"); - if (options.steps === undefined ) throw new Error("steps should be provided"); - if(options.start === undefined) { - this._fetchKeySpan(); - } else { - this._setReady(true); - } - }; - - json.prototype = { - - /** - * return the torque tile encoded in an efficient javascript - * structure: - * { - * x:Uint8Array x coordinates in tile reference system, normally from 0-255 - * y:Uint8Array y coordinates in tile reference system - * Index: Array index to the properties - * } - */ - proccessTile: function(rows, coord, zoom) { - var r; - var x = new Uint8Array(rows.length); - var y = new Uint8Array(rows.length); - - var prof_mem = Profiler.metric('ProviderJSON:mem'); - var prof_point_count = Profiler.metric('ProviderJSON:point_count'); - var prof_process_time = Profiler.metric('ProviderJSON:process_time').start() - - // count number of dates - var dates = 0; - var maxDateSlots = -1; - for (r = 0; r < rows.length; ++r) { - var row = rows[r]; - dates += row.dates__uint16.length; - for(var d = 0; d < row.dates__uint16.length; ++d) { - maxDateSlots = Math.max(maxDateSlots, row.dates__uint16[d]); - } - } - - if(this.options.cumulative) { - dates = (1 + maxDateSlots) * rows.length; - } - - var type = this.options.cumulative ? Uint32Array: Uint8Array; - - // reserve memory for all the dates - var timeIndex = new Int32Array(maxDateSlots + 1); //index-size - var timeCount = new Int32Array(maxDateSlots + 1); - var renderData = new (this.options.valueDataType || type)(dates); - var renderDataPos = new Uint32Array(dates); - - prof_mem.inc( - 4 * maxDateSlots + // timeIndex - 4 * maxDateSlots + // timeCount - dates + //renderData - dates * 4 - ); //renderDataPos - - prof_point_count.inc(rows.length); - - var rowsPerSlot = {}; - - // precache pixel positions - for (var r = 0; r < rows.length; ++r) { - var row = rows[r]; - x[r] = row.x__uint8 * this.options.resolution; - // fix value when it's in the tile EDGE - // TODO: this should be fixed in SQL query - if (row.y__uint8 === -1) { - y[r] = 0; - } else { - y[r] = row.y__uint8 * this.options.resolution; - } - - var dates = row.dates__uint16; - var vals = row.vals__uint8; - if (!this.options.cumulative) { - for (var j = 0, len = dates.length; j < len; ++j) { - var rr = rowsPerSlot[dates[j]] || (rowsPerSlot[dates[j]] = []); - if(this.options.cumulative) { - vals[j] += prev_val; - } - prev_val = vals[j]; - rr.push([r, vals[j]]); - } - } else { - var valByDate = {} - for (var j = 0, len = dates.length; j < len; ++j) { - valByDate[dates[j]] = vals[j]; - } - var accum = 0; - - // extend the latest to the end - for (var j = dates[0]; j <= maxDateSlots; ++j) { - var rr = rowsPerSlot[j] || (rowsPerSlot[j] = []); - var v = valByDate[j]; - if (v) { - accum += v; - } - rr.push([r, accum]); - } - - /*var lastDateSlot = dates[dates.length - 1]; - for (var j = lastDateSlot + 1; j <= maxDateSlots; ++j) { - var rr = rowsPerSlot[j] || (rowsPerSlot[j] = []); - rr.push([r, prev_val]); - } - */ - } - - } - - // for each timeslot search active buckets - var renderDataIndex = 0; - var timeSlotIndex = 0; - var i = 0; - for(var i = 0; i <= maxDateSlots; ++i) { - var c = 0; - var slotRows = rowsPerSlot[i] - if(slotRows) { - for (var r = 0; r < slotRows.length; ++r) { - var rr = slotRows[r]; - ++c; - renderDataPos[renderDataIndex] = rr[0] - renderData[renderDataIndex] = rr[1]; - ++renderDataIndex; - } - } - timeIndex[i] = timeSlotIndex; - timeCount[i] = c; - timeSlotIndex += c; - } - - prof_process_time.end(); - - return { - x: x, - y: y, - z: zoom, - coord: { - x: coord.x, - y: coord.y, - z: zoom - }, - timeCount: timeCount, - timeIndex: timeIndex, - renderDataPos: renderDataPos, - renderData: renderData, - maxDate: maxDateSlots - }; - }, - - _host: function() { - var opts = this.options; - var port = opts.sql_api_port; - var domain = ((opts.user_name || opts.user) + '.' + (opts.sql_api_domain || 'cartodb.com')) + (port ? ':' + port: ''); - var protocol = opts.sql_api_protocol || 'http'; - return this.options.url || protocol + '://' + domain + '/api/v2/sql'; - }, - - url: function(subhost) { - var opts = this.options; - var protocol = opts.sql_api_protocol || 'http'; - if (!this.options.cdn_url) { - return this._host(); - } - var h = protocol+ "://"; - if (subhost) { - h += subhost + "."; - } - var cdn_host = opts.cdn_url; - if(!cdn_host.http && !cdn_host.https) { - throw new Error("cdn_host should contain http and/or https entries"); - } - h += cdn_host[protocol] + "/" + (opts.user_name || opts.user) + '/api/v2/sql'; - return h; - }, - - _hash: function(str) { - var hash = 0; - if (!str || str.length == 0) return hash; - for (var i = 0, l = str.length; i < l; ++i) { - hash = (( (hash << 5 ) - hash ) + str.charCodeAt(i)) | 0; - } - return hash; - }, - - _extraParams: function() { - if (this.options.extra_params) { - var p = []; - for(var k in this.options.extra_params) { - var v = this.options.extra_params[k]; - if (v) { - p.push(k + "=" + encodeURIComponent(v)); - } - } - return p.join('&'); - } - return null; - }, - - isHttps: function() { - return this.options.sql_api_protocol && this.options.sql_api_protocol === 'https'; - }, - - // execute actual query - sql: function(sql, callback, options) { - options = options || {}; - var subdomains = this.options.subdomains || '0123'; - if(this.isHttps()) { - subdomains = [null]; // no subdomain - } - - - var url; - if (options.no_cdn) { - url = this._host(); - } else { - url = this.url(subdomains[Math.abs(this._hash(sql))%subdomains.length]); - } - var extra = this._extraParams(); - torque.net.get( url + "?q=" + encodeURIComponent(sql) + (extra ? "&" + extra: ''), function (data) { - if(options.parseJSON) { - data = JSON.parse(data && data.responseText); - } - callback && callback(data); - }); - }, - - getTileData: function(coord, zoom, callback) { - if(!this._ready) { - this._tileQueue.push([coord, zoom, callback]); - } else { - this._getTileData(coord, zoom, callback); - } - }, - - _setReady: function(ready) { - this._ready = true; - this._processQueue(); - this.options.ready && this.options.ready(); - }, - - _processQueue: function() { - var item; - while (item = this._tileQueue.pop()) { - this._getTileData.apply(this, item); - } - }, - - /** - * `coord` object like {x : tilex, y: tiley } - * `zoom` quadtree zoom level - */ - _getTileData: function(coord, zoom, callback) { - var prof_fetch_time = Profiler.metric('ProviderJSON:tile_fetch_time').start() - this.table = this.options.table; - var numTiles = 1 << zoom; - - var column_conv = this.options.column; - - if(this.options.is_time) { - column_conv = format("date_part('epoch', {column})", this.options); - } - - var sql = "" + - "WITH " + - "par AS (" + - " SELECT CDB_XYZ_Resolution({zoom})*{resolution} as res" + - ", 256/{resolution} as tile_size" + - ", CDB_XYZ_Extent({x}, {y}, {zoom}) as ext " + - ")," + - "cte AS ( "+ - " SELECT ST_SnapToGrid(i.the_geom_webmercator, p.res) g" + - ", {countby} c" + - ", floor(({column_conv} - {start})/{step}) d" + - " FROM ({_sql}) i, par p " + - " WHERE i.the_geom_webmercator && p.ext " + - " GROUP BY g, d" + - ") " + - "" + - "SELECT (st_x(g)-st_xmin(p.ext))/p.res x__uint8, " + - " (st_y(g)-st_ymin(p.ext))/p.res y__uint8," + - " array_agg(c) vals__uint8," + - " array_agg(d) dates__uint16" + - // the tile_size where are needed because the overlaps query in cte subquery includes the points - // in the left and bottom borders of the tile - " FROM cte, par p where (st_y(g)-st_ymin(p.ext))/p.res < tile_size and (st_x(g)-st_xmin(p.ext))/p.res < tile_size GROUP BY x__uint8, y__uint8"; - - - var query = format(sql, this.options, { - zoom: zoom, - x: coord.x, - y: coord.y, - column_conv: column_conv, - _sql: this.getSQL() - }); - - var self = this; - this.sql(query, function (data) { - if (data) { - var rows = JSON.parse(data.responseText).rows; - callback(self.proccessTile(rows, coord, zoom)); - } else { - callback(null); - } - prof_fetch_time.end(); - }); - }, - - getKeySpan: function() { - return { - start: this.options.start * 1000, - end: this.options.end * 1000, - step: this.options.step, - steps: this.options.steps, - columnType: this.options.is_time ? 'date': 'number' - }; - }, - - setColumn: function(column, isTime) { - this.options.column = column; - this.options.is_time = isTime === undefined ? true: false; - this.reload(); - }, - - setResolution: function(res) { - this.options.resolution = res; - }, - - // return true if tiles has been changed - setOptions: function(opt) { - var refresh = false; - - if(opt.resolution !== undefined && opt.resolution !== this.options.resolution) { - this.options.resolution = opt.resolution; - refresh = true; - } - - if(opt.steps !== undefined && opt.steps !== this.options.steps) { - this.setSteps(opt.steps, { silent: true }); - refresh = true; - } - - if(opt.column !== undefined && opt.column !== this.options.column) { - this.options.column = opt.column; - refresh = true; - } - - if(opt.countby !== undefined && opt.countby !== this.options.countby) { - this.options.countby = opt.countby; - refresh = true; - } - - if(opt.data_aggregation !== undefined) { - var c = opt.data_aggregation === 'cumulative'; - if (this.options.cumulative !== c) { - this.options.cumulative = c; - refresh = true; - } - } - - if (refresh) this.reload(); - return refresh; - - }, - - reload: function() { - this._ready = false; - this._fetchKeySpan(); - }, - - setSQL: function(sql) { - if (this.options.sql != sql) { - this.options.sql = sql; - this.reload(); - } - }, - - getSteps: function() { - return Math.min(this.options.steps, this.options.data_steps); - }, - - setSteps: function(steps, opt) { - opt = opt || {}; - if (this.options.steps !== steps) { - this.options.steps = steps; - this.options.step = (this.options.end - this.options.start)/this.getSteps(); - this.options.step = this.options.step || 1; - if (!opt.silent) this.reload(); - } - }, - - getBounds: function() { - return this.options.bounds; - }, - - getSQL: function() { - return this.options.sql || "select * from " + this.options.table; - }, - - _tilerHost: function() { - var opts = this.options; - var user = (opts.user_name || opts.user); - return opts.tiler_protocol + - "://" + (user ? user + "." : "") + - opts.tiler_domain + - ((opts.tiler_port != "") ? (":" + opts.tiler_port) : ""); - }, - - _fetchUpdateAt: function(callback) { - var self = this; - var layergroup = { - "version": "1.0.1", - "stat_tag": this.options.stat_tag || 'torque', - "layers": [{ - "type": "cartodb", - "options": { - "cartocss_version": "2.1.1", - "cartocss": "#layer {}", - "sql": this.getSQL() - } - }] - }; - var url = this._tilerHost() + "/tiles/layergroup"; - var extra = this._extraParams(); - - // tiler needs map_key instead of api_key - // so replace it - if (extra) { - extra = extra.replace('api_key=', 'map_key='); - } - - url = url + - "?config=" + encodeURIComponent(JSON.stringify(layergroup)) + - "&callback=?" + (extra ? "&" + extra: ''); - - torque.net.jsonp(url, function (data) { - var query = format("select * from ({sql}) __torque_wrap_sql limit 0", { sql: self.getSQL() }); - self.sql(query, function (queryData) { - if (data) { - callback({ - updated_at: data.last_updated, - fields: queryData.fields - }); - } - }, { parseJSON: true }); - }); - }, - - // - // the data range could be set by the user though ``start`` - // option. It can be fecthed from the table when the start - // is not specified. - // - _fetchKeySpan: function() { - var self = this; - var max_col, min_col, max_tmpl, min_tmpl; - - this._fetchUpdateAt(function(data) { - if (!data) return; - self.options.extra_params = self.options.extra_params || {}; - self.options.extra_params.last_updated = data.updated_at || 0; - self.options.extra_params.cache_policy = 'persist'; - self.options.is_time = data.fields[self.options.column].type === 'date'; - - var column_conv = self.options.column; - if (self.options.is_time){ - max_tmpl = "date_part('epoch', max({column}))"; - min_tmpl = "date_part('epoch', min({column}))"; - column_conv = format("date_part('epoch', {column})", self.options); - } else { - max_tmpl = "max({column})"; - min_tmpl = "min({column})"; - } - - max_col = format(max_tmpl, { column: self.options.column }); - min_col = format(min_tmpl, { column: self.options.column }); - - /*var sql_stats = "" + - "WITH summary_groups as ( " + - "WITH summary as ( " + - "select (row_number() over (order by __time_col asc nulls last)+1)/2 as rownum, __time_col " + - "from (select *, {column} as __time_col from ({sql}) __s) __torque_wrap_sql " + - "order by __time_col asc " + - ") " + - "SELECT " + - "max(__time_col) OVER(PARTITION BY rownum) - " + - "min(__time_col) OVER(PARTITION BY rownum) diff " + - "FROM summary " + - "), subq as ( " + - " SELECT " + - "st_xmax(st_envelope(st_collect(the_geom))) xmax, " + - "st_ymax(st_envelope(st_collect(the_geom))) ymax, " + - "st_xmin(st_envelope(st_collect(the_geom))) xmin, " + - "st_ymin(st_envelope(st_collect(the_geom))) ymin, " + - "{max_col} max, " + - "{min_col} min FROM ({sql}) __torque_wrap_sql " + - ")" + - "SELECT " + - "xmax, xmin, ymax, ymin, a.max as max_date, a.min as min_date, " + - "avg(diff) as diffavg," + - "(a.max - a.min)/avg(diff) as num_steps " + - "FROM summary_groups, subq a " + - "WHERE diff > 0 group by xmax, xmin, ymax, ymin, max_date, min_date"; - */ - var sql_stats = " SELECT " + - "st_xmax(st_envelope(st_collect(the_geom))) xmax, " + - "st_ymax(st_envelope(st_collect(the_geom))) ymax, " + - "st_xmin(st_envelope(st_collect(the_geom))) xmin, " + - "st_ymin(st_envelope(st_collect(the_geom))) ymin, " + - "count(*) as num_steps, " + - "{max_col} max_date, " + - "{min_col} min_date FROM ({sql}) __torque_wrap_sql "; - - var sql = format(sql_stats, { - max_col: max_col, - min_col: min_col, - column: column_conv, - sql: self.getSQL() - }); - - self.sql(sql, function(data) { - //TODO: manage bounds - data = data.rows[0]; - self.options.start = data.min_date; - self.options.end = data.max_date; - self.options.step = (data.max_date - data.min_date)/Math.min(self.options.steps, data.num_steps>>0); - self.options.data_steps = data.num_steps >> 0; - // step can't be 0 - self.options.step = self.options.step || 1; - self.options.bounds = [ - [data.ymin, data.xmin], - [data.ymax, data.xmax] - ]; - self._setReady(true); - }, { parseJSON: true, no_cdn: true }); - }, { parseJSON: true, no_cdn: true}) - } - - }; - - torque.providers.json = json; - - -})(typeof exports === "undefined" ? this : exports); -(function(exports) { - - - var torque = exports.torque = exports.torque || {}; - var providers = exports.torque.providers = exports.torque.providers || {}; - - var Uint8Array = torque.types.Uint8Array; - var Int32Array = torque.types.Int32Array; - var Uint32Array = torque.types.Uint32Array; - - // format('hello, {0}', 'rambo') -> "hello, rambo" - function format(str, attrs) { - for(var i = 1; i < arguments.length; ++i) { - var attrs = arguments[i]; - for(var attr in attrs) { - str = str.replace(RegExp('\\{' + attr + '\\}', 'g'), attrs[attr]); - } - } - return str; - } - - var json = function (options) { - // check options - this.options = options; - }; - - - json.prototype = { - - // - // return the data aggregated by key: - // { - // key0: 12, - // key1: 32 - // key2: 25 - // } - // - aggregateByKey: function(rows) { - function getKeys(row) { - var HEADER_SIZE = 3; - var valuesCount = row.data[2]; - var keys = {}; - for (var s = 0; s < valuesCount; ++s) { - keys[row.data[HEADER_SIZE + s]] = row.data[HEADER_SIZE + valuesCount + s]; - } - return keys; - } - var keys = {}; - for (r = 0; r < rows.length; ++r) { - var rowKeys = getKeys(rows[r]); - for(var k in rowKeys) { - keys[k] = keys[k] || 0; - keys[k] += rowKeys[k]; - } - } - return keys; - }, - - - - - /** - * - */ - proccessTile: function(rows, coord, zoom) { - var r; - var x = new Uint8Array(rows.length); - var y = new Uint8Array(rows.length); - var self = this; - - // decode into a javascript strcuture the array - function decode_row(row) { - var HEADER_SIZE = 3; - var o = { - x: row.data[0] * self.options.resolution, - y: row.data[1] * self.options.resolution, - valuesCount: row.data[2], - times: [], - values: [] - }; - for (var s = 0; s < o.valuesCount; ++s) { - o.times.push(row.data[HEADER_SIZE + s]); - o.values.push(row.data[HEADER_SIZE + o.valuesCount + s]); - } - if(self.options.cumulative) { - for (var s = 1; s < o.valuesCount; ++s) { - o.values[s] += o.values[s - 1]; - } - } - return o - } - - // decode all the rows - for (r = 0; r < rows.length; ++r) { - rows[r] = decode_row(rows[r]); - } - - // count number of dates - var dates = 0; - var maxDateSlots = 0; - for (r = 0; r < rows.length; ++r) { - var row = rows[r]; - dates += row.times.length; - for(var d = 0; d < row.times.length; ++d) { - maxDateSlots = Math.max(maxDateSlots, row.times[d]); - } - } - - // reserve memory for all the dates - var timeIndex = new Int32Array(maxDateSlots + 1); //index-size - var timeCount = new Int32Array(maxDateSlots + 1); - var renderData = new (this.options.valueDataType || Uint8Array)(dates); - var renderDataPos = new Uint32Array(dates); - - var rowsPerSlot = {}; - - // precache pixel positions - for (var r = 0; r < rows.length; ++r) { - var row = rows[r]; - x[r] = row.x; - y[r] = row.y; - - var dates = row.times; - var vals = row.values; - for (var j = 0, len = dates.length; j < len; ++j) { - var rr = rowsPerSlot[dates[j]] || (rowsPerSlot[dates[j]] = []); - rr.push([r, vals[j]]); - } - } - - // for each timeslot search active buckets - var renderDataIndex = 0; - var timeSlotIndex = 0; - var i = 0; - for(var i = 0; i <= maxDateSlots; ++i) { - var c = 0; - var slotRows = rowsPerSlot[i] - if(slotRows) { - for (var r = 0; r < slotRows.length; ++r) { - var rr = slotRows[r]; - ++c; - renderDataPos[renderDataIndex] = rr[0] - renderData[renderDataIndex] = rr[1]; - ++renderDataIndex; - } - } - timeIndex[i] = timeSlotIndex; - timeCount[i] = c; - timeSlotIndex += c; - } - - return { - x: x, - y: y, - coord: { - x: coord.x, - y: coord.y, - z: zoom - }, - timeCount: timeCount, - timeIndex: timeIndex, - renderDataPos: renderDataPos, - renderData: renderData - }; - }, - - url: function() { - return this.options.url; - }, - - - tileUrl: function(coord, zoom) { - var template = this.url(); - var s = (this.options.subdomains || 'abcd')[(coord.x + coord.y + zoom) % 4]; - return template - .replace('{x}', coord.x) - .replace('{y}', coord.y) - .replace('{z}', zoom) - .replace('{s}', s); - }, - - getTile: function(coord, zoom, callback) { - var template = this.tileUrl(coord, zoom); - - var self = this; - var fetchTime = Profiler.metric('jsonarray:fetch time'); - fetchTime.start(); - torque.net.get(template, function (data) { - fetchTime.end(); - if(data) { - data = JSON.parse(data.responseText); - } - callback(data); - }); - }, - - /** - * `coord` object like {x : tilex, y: tiley } - * `zoom` quadtree zoom level - */ - getTileData: function(coord, zoom, callback) { - var template = this.tileUrl(coord, zoom); - - var self = this; - var fetchTime = Profiler.metric('jsonarray:fetch time'); - fetchTime.start(); - torque.net.get(template, function (data) { - fetchTime.end(); - var processed = null; - - var processingTime = Profiler.metric('jsonarray:processing time'); - var parsingTime = Profiler.metric('jsonarray:parsing time'); - try { - processingTime.start(); - parsingTime.start(); - var rows = JSON.parse(data.responseText || data.response).rows; - parsingTime.end(); - processed = self.proccessTile(rows, coord, zoom); - processingTime.end(); - } catch(e) { - console.error("problem parsing JSON on ", coord, zoom); - } - - callback(processed); - - }); - } - - }; - - torque.providers.JsonArray = json - - -})(typeof exports === "undefined" ? this : exports); -(function(exports) { - - var torque = exports.torque = exports.torque || {}; - var providers = exports.torque.providers = exports.torque.providers || {}; - - var Uint8Array = torque.types.Uint8Array; - var Int32Array = torque.types.Int32Array; - var Uint32Array = torque.types.Uint32Array; - - // format('hello, {0}', 'rambo') -> "hello, rambo" - function format(str) { - for(var i = 1; i < arguments.length; ++i) { - var attrs = arguments[i]; - for(var attr in attrs) { - str = str.replace(RegExp('\\{' + attr + '\\}', 'g'), attrs[attr]); - } - } - return str; - } - - var json = function (options) { - this._ready = false; - this._tileQueue = []; - this.options = options; - - this.options.is_time = this.options.is_time === undefined ? true: this.options.is_time; - this.options.tiler_protocol = options.tiler_protocol || 'http'; - this.options.tiler_domain = options.tiler_domain || 'cartodb.com'; - this.options.tiler_port = options.tiler_port || 80; - - if (this.options.data_aggregation) { - this.options.cumulative = this.options.data_aggregation === 'cumulative'; - } - if (this.options.auth_token) { - var e = this.options.extra_params || (this.options.extra_params = {}); - e.auth_token = this.options.auth_token; - } - - this._fetchMap(); - }; - - json.prototype = { - - /** - * return the torque tile encoded in an efficient javascript - * structure: - * { - * x:Uint8Array x coordinates in tile reference system, normally from 0-255 - * y:Uint8Array y coordinates in tile reference system - * Index: Array index to the properties - * } - */ - proccessTile: function(rows, coord, zoom) { - var r; - var x = new Uint8Array(rows.length); - var y = new Uint8Array(rows.length); - - var prof_mem = Profiler.metric('torque.provider.windshaft.mem'); - var prof_point_count = Profiler.metric('torque.provider.windshaft.points'); - var prof_process_time = Profiler.metric('torque.provider.windshaft.process_time').start(); - - // count number of dates - var dates = 0; - var maxDateSlots = -1; - for (r = 0; r < rows.length; ++r) { - var row = rows[r]; - dates += row.dates__uint16.length; - for(var d = 0; d < row.dates__uint16.length; ++d) { - maxDateSlots = Math.max(maxDateSlots, row.dates__uint16[d]); - } - } - - if(this.options.cumulative) { - dates = (1 + maxDateSlots) * rows.length; - } - - var type = this.options.cumulative ? Uint32Array: Uint8Array; - - // reserve memory for all the dates - var timeIndex = new Int32Array(maxDateSlots + 1); //index-size - var timeCount = new Int32Array(maxDateSlots + 1); - var renderData = new (this.options.valueDataType || type)(dates); - var renderDataPos = new Uint32Array(dates); - - prof_mem.inc( - 4 * maxDateSlots + // timeIndex - 4 * maxDateSlots + // timeCount - dates + //renderData - dates * 4 - ); //renderDataPos - - prof_point_count.inc(rows.length); - - var rowsPerSlot = {}; - - // precache pixel positions - for (var r = 0; r < rows.length; ++r) { - var row = rows[r]; - x[r] = row.x__uint8 * this.options.resolution; - // fix value when it's in the tile EDGE - // TODO: this should be fixed in SQL query - if (row.y__uint8 === -1) { - y[r] = 0; - } else { - y[r] = row.y__uint8 * this.options.resolution; - } - - var dates = row.dates__uint16; - var vals = row.vals__uint8; - if (!this.options.cumulative) { - for (var j = 0, len = dates.length; j < len; ++j) { - var rr = rowsPerSlot[dates[j]] || (rowsPerSlot[dates[j]] = []); - if(this.options.cumulative) { - vals[j] += prev_val; - } - prev_val = vals[j]; - rr.push([r, vals[j]]); - } - } else { - var valByDate = {} - for (var j = 0, len = dates.length; j < len; ++j) { - valByDate[dates[j]] = vals[j]; - } - var accum = 0; - - // extend the latest to the end - for (var j = dates[0]; j <= maxDateSlots; ++j) { - var rr = rowsPerSlot[j] || (rowsPerSlot[j] = []); - var v = valByDate[j]; - if (v) { - accum += v; - } - rr.push([r, accum]); - } - - /*var lastDateSlot = dates[dates.length - 1]; - for (var j = lastDateSlot + 1; j <= maxDateSlots; ++j) { - var rr = rowsPerSlot[j] || (rowsPerSlot[j] = []); - rr.push([r, prev_val]); - } - */ - } - - } - - // for each timeslot search active buckets - var renderDataIndex = 0; - var timeSlotIndex = 0; - var i = 0; - for(var i = 0; i <= maxDateSlots; ++i) { - var c = 0; - var slotRows = rowsPerSlot[i] - if(slotRows) { - for (var r = 0; r < slotRows.length; ++r) { - var rr = slotRows[r]; - ++c; - renderDataPos[renderDataIndex] = rr[0] - renderData[renderDataIndex] = rr[1]; - ++renderDataIndex; - } - } - timeIndex[i] = timeSlotIndex; - timeCount[i] = c; - timeSlotIndex += c; - } - - prof_process_time.end(); - - return { - x: x, - y: y, - z: zoom, - coord: { - x: coord.x, - y: coord.y, - z: zoom - }, - timeCount: timeCount, - timeIndex: timeIndex, - renderDataPos: renderDataPos, - renderData: renderData, - maxDate: maxDateSlots - }; - }, - - /*setCartoCSS: function(c) { - this.options.cartocss = c; - },*/ - - setSteps: function(steps, opt) { - opt = opt || {}; - if (this.options.steps !== steps) { - this.options.steps = steps; - this.options.step = (this.options.end - this.options.start)/this.getSteps(); - this.options.step = this.options.step || 1; - if (!opt.silent) this.reload(); - } - }, - - setOptions: function(opt) { - var refresh = false; - - if(opt.resolution !== undefined && opt.resolution !== this.options.resolution) { - this.options.resolution = opt.resolution; - refresh = true; - } - - if(opt.steps !== undefined && opt.steps !== this.options.steps) { - this.setSteps(opt.steps, { silent: true }); - refresh = true; - } - - if(opt.column !== undefined && opt.column !== this.options.column) { - this.options.column = opt.column; - refresh = true; - } - - if(opt.countby !== undefined && opt.countby !== this.options.countby) { - this.options.countby = opt.countby; - refresh = true; - } - - if(opt.data_aggregation !== undefined) { - var c = opt.data_aggregation === 'cumulative'; - if (this.options.cumulative !== c) { - this.options.cumulative = c; - refresh = true; - } - } - - if (refresh) this.reload(); - return refresh; - }, - - _extraParams: function(e) { - e = torque.extend(torque.extend({}, e), this.options.extra_params); - if (e) { - var p = []; - for(var k in e) { - var v = e[k]; - if (v) { - if (torque.isArray(v)) { - for (var i = 0, len = v.length; i < len; i++) { - p.push(k + "[]=" + encodeURIComponent(v[i])); - } - } else { - p.push(k + "=" + encodeURIComponent(v)); - } - } - } - return p.join('&'); - } - return null; - }, - - getTileData: function(coord, zoom, callback) { - if(!this._ready) { - this._tileQueue.push([coord, zoom, callback]); - } else { - this._getTileData(coord, zoom, callback); - } - }, - - _setReady: function(ready) { - this._ready = true; - this._processQueue(); - this.options.ready && this.options.ready(); - }, - - _processQueue: function() { - var item; - while (item = this._tileQueue.pop()) { - this._getTileData.apply(this, item); - } - }, - - /** - * `coord` object like {x : tilex, y: tiley } - * `zoom` quadtree zoom level - */ - _getTileData: function(coord, zoom, callback) { - var self = this; - var prof_fetch_time = Profiler.metric('torque.provider.windshaft.tile.fetch').start(); - var subdomains = this.options.subdomains || '0123'; - var index = Math.abs(coord.x + coord.y) % subdomains.length; - var url = this.templateUrl - .replace('{x}', coord.x) - .replace('{y}', coord.y) - .replace('{z}', zoom) - .replace('{s}', subdomains[index]) - - var extra = this._extraParams(); - torque.net.get( url + (extra ? "?" + extra: ''), function (data) { - prof_fetch_time.end(); - if (data && data.responseText) { - var rows = JSON.parse(data.responseText); - callback(self.proccessTile(rows, coord, zoom)); - } else { - Profiler.metric('torque.provider.windshaft.tile.error').inc(); - callback(null); - } - }); - }, - - getKeySpan: function() { - return { - start: this.options.start, - end: this.options.end, - step: this.options.step, - steps: this.options.steps, - columnType: this.options.column_type - }; - }, - - setColumn: function(column, isTime) { - this.options.column = column; - this.options.is_time = isTime === undefined ? true: false; - this.reload(); - }, - - reload: function() { - this._ready = false; - this._fetchMap(); - }, - - getSteps: function() { - return Math.min(this.options.steps, this.options.data_steps); - }, - - getBounds: function() { - return this.options.bounds; - }, - - getSQL: function() { - return this.options.sql || "select * from " + this.options.table; - }, - - setSQL: function(sql) { - if (this.options.sql != sql) { - this.options.sql = sql; - this.reload(); - } - }, - - _tilerHost: function() { - var opts = this.options; - var user = (opts.user_name || opts.user); - return opts.tiler_protocol + - "://" + (user ? user + "." : "") + - opts.tiler_domain + - ((opts.tiler_port != "") ? (":" + opts.tiler_port) : ""); - }, - - url: function() { - var opts = this.options; - var protocol = opts.tiler_protocol || 'http'; - if (!this.options.cdn_url || this.options.no_cdn) { - return this._tilerHost(); - } - var h = protocol + "://" - if (protocol === 'http') { - h += "{s}."; - } - var cdn_host = opts.cdn_url; - if(!cdn_host.http && !cdn_host.https) { - throw new Error("cdn_host should contain http and/or https entries"); - } - h += cdn_host[protocol] + "/" + (opts.user_name || opts.user); - return h; - }, - - _generateCartoCSS: function() { - var attr = { - '-torque-frame-count': this.options.steps, - '-torque-resolution': this.options.resolution, - '-torque-aggregation-function': "'" + this.options.countby + "'", - '-torque-time-attribute': "'" + this.options.column + "'", - '-torque-data-aggregation': this.options.cumulative ? 'cumulative': 'linear', - }; - var st = 'Map{'; - for (var k in attr) { - st += k + ":" + attr[k] + ";"; - } - return st + "}"; - }, - - _fetchMap: function(callback) { - var self = this; - var layergroup = {}; - var host = this.options.dynamic_cdn ? this.url().replace('{s}', '0'): this._tilerHost(); - var url = host + "/api/v1/map"; - var named = this.options.named_map; - - if(named) { - //tiles/template - url = host + "/api/v1/map/named/" + named.name + "/jsonp"; - } else { - layergroup = { - "version": "1.0.1", - "stat_tag": this.options.stat_tag || 'torque', - "layers": [{ - "type": "torque", - "options": { - "cartocss_version": "1.0.0", - "cartocss": this._generateCartoCSS(), - "sql": this.getSQL() - } - }] - }; - } - var extra = this._extraParams(this.options.stat_tag ? { stat_tag: this.options.stat_tag }: {} ); - - // tiler needs map_key instead of api_key - // so replace it - if (extra) { - extra = extra.replace('api_key=', 'map_key='); - } - - url = url + - "?config=" + encodeURIComponent(JSON.stringify(layergroup)) + - "&callback=?" + (extra ? "&" + extra: ''); - - var map_instance_time = Profiler.metric('torque.provider.windshaft.layergroup.time').start(); - torque.net.jsonp(url, function (data) { - map_instance_time.end(); - if (data) { - var torque_key = Object.keys(data.metadata.torque)[0] - var opt = data.metadata.torque[torque_key]; - for(var k in opt) { - self.options[k] = opt[k]; - } - // use cdn_url if present - if (data.cdn_url) { - var c = self.options.cdn_url = self.options.cdn_url || {}; - c.http = data.cdn_url.http || c.http; - c.https = data.cdn_url.https || c.https; - } - self.templateUrl = self.url() + "/api/v1/map/" + data.layergroupid + "/" + torque_key + "/{z}/{x}/{y}.json.torque"; - self._setReady(true); - } else { - Profiler.metric('torque.provider.windshaft.layergroup.error').inc(); - } - }, { callbackName: self.options.instanciateCallback }); - } - - }; - - torque.providers.windshaft = json; - - -})(typeof exports === "undefined" ? this : exports); -(function(exports) { - var torque = exports.torque = exports.torque || {}; - torque.net = torque.net || {}; - - var lastCall = null; - - function jsonp(url, callback, options) { - options = options || {}; - options.timeout = options.timeout === undefined ? 10000: options.timeout; - var head = document.getElementsByTagName('head')[0]; - var script = document.createElement('script'); - - // function name - var fnName = options.callbackName || 'torque_' + Date.now(); - - if (torque.isFunction(fnName)) { - fnName = fnName(); - } - - function clean() { - head.removeChild(script); - clearTimeout(timeoutTimer); - delete window[fnName]; - } - - window[fnName] = function() { - clean(); - callback.apply(window, arguments); - }; - - // timeout for errors - var timeoutTimer = setTimeout(function() { - clean(); - callback.call(window, null); - }, options.timeout); - - // setup url - url = url.replace('callback=\?', 'callback=' + fnName); - script.type = 'text/javascript'; - script.src = url; - script.async = true; - // defer the loading because IE9 loads in the same frame the script - // so Loader._script is null - setTimeout(function() { head.appendChild(script); }, 0); - } - - function get(url, callback, options) { - options = options || { - method: 'GET', - data: null, - responseType: 'text' - }; - lastCall = { url: url, callback: callback }; - var request = XMLHttpRequest; - // from d3.js - if (window.XDomainRequest - && !("withCredentials" in request) - && /^(http(s)?:)?\/\//.test(url)) request = XDomainRequest; - - var req = new request(); - req.open(options.method, url, true); - - - function respond() { - var status = req.status, result; - var r = options.responseType === 'arraybuffer' ? req.response: req.responseText; - if (!status && r || status >= 200 && status < 300 || status === 304) { - callback(req); - } else { - callback(null); - } - } - - "onload" in req - ? req.onload = req.onerror = respond - : req.onreadystatechange = function() { req.readyState > 3 && respond(); }; - - req.onprogress = function() {}; - - req.responseType = options.responseType; //'arraybuffer'; - if (options.data) { - req.setRequestHeader("Content-type", "application/json"); - //req.setRequestHeader("Content-type", "application/x-www-form-urlencoded") - req.setRequestHeader("Accept", "*"); - } - req.send(options.data); - return req; - } - - function post(url, data, callback) { - return get(url, callback, { - data: data, - method: "POST" - }); - } - - torque.net = { - get: get, - post: post, - jsonp: jsonp, - lastCall: function() { return lastCall; } - }; - -})(typeof exports === "undefined" ? this : exports); -(function(exports) { - - exports.torque = exports.torque || {}; - - var TAU = Math.PI*2; - // min value to render a line. - // it does not make sense to render a line of a width is not even visible - var LINEWIDTH_MIN_VALUE = 0.05; - - function renderPoint(ctx, st) { - ctx.fillStyle = st.fillStyle; - var pixel_size = st['point-radius']; - - // render a circle - - // fill - ctx.beginPath(); - ctx.arc(0, 0, pixel_size, 0, TAU, true, true); - ctx.closePath(); - if (st.fillStyle) { - if (st.fillOpacity) { - ctx.globalAlpha = st.fillOpacity; - } - ctx.fill(); - } - - // stroke - ctx.globalAlpha = 1.0; - if (st.strokeStyle && st.lineWidth && st.lineWidth > LINEWIDTH_MIN_VALUE) { - if (st.strokeOpacity) { - ctx.globalAlpha = st.strokeOpacity; - } - if (st.lineWidth) { - ctx.lineWidth = st.lineWidth; - } - ctx.strokeStyle = st.strokeStyle; - - // do not render for alpha = 0 - if (ctx.globalAlpha > 0) { - ctx.stroke(); - } - } - } - - function renderRectangle(ctx, st) { - ctx.fillStyle = st.fillStyle; - var pixel_size = st['point-radius']; - var w = pixel_size * 2; - - // fill - if (st.fillStyle && st.fillOpacity) { - ctx.globalAlpha = st.fillOpacity; - } - ctx.fillRect(-pixel_size, -pixel_size, w, w) - - // stroke - ctx.globalAlpha = 1.0; - if (st.strokeStyle && st.lineWidth) { - if (st.strokeOpacity) { - ctx.globalAlpha = st.strokeOpacity; - } - if (st.lineWidth) { - ctx.lineWidth = st.lineWidth; - } - ctx.strokeStyle = st.strokeStyle; - - // do not render for alpha = 0 - if (ctx.globalAlpha > 0) { - ctx.strokeRect(-pixel_size, -pixel_size, w, w) - } - } - } - - function renderSprite(ctx, st) { - var img = st['point-file'] || st['marker-file']; - var ratio = img.height/img.width; - var w = st['point-radius'] || img.width; - var h = st['point-radius'] || st['marker-height'] || w*ratio; - ctx.drawImage(img, 0, 0, w, h); - } - - exports.torque.cartocss = exports.torque.cartocss || {}; - exports.torque.cartocss = { - renderPoint: renderPoint, - renderSprite: renderSprite, - renderRectangle: renderRectangle - }; - -})(typeof exports === "undefined" ? this : exports); -(function(exports) { - exports.torque = exports.torque || {}; - exports.torque.renderer = exports.torque.renderer || {}; - - var TAU = Math.PI * 2; - var DEFAULT_CARTOCSS = [ - '#layer {', - ' marker-fill: #662506;', - ' marker-width: 4;', - ' [value > 1] { marker-fill: #FEE391; }', - ' [value > 2] { marker-fill: #FEC44F; }', - ' [value > 3] { marker-fill: #FE9929; }', - ' [value > 4] { marker-fill: #EC7014; }', - ' [value > 5] { marker-fill: #CC4C02; }', - ' [value > 6] { marker-fill: #993404; }', - ' [value > 7] { marker-fill: #662506; }', - '}' - ].join('\n'); - - // - // this renderer just render points depending of the value - // - function PointRenderer(canvas, options) { - if (!canvas) { - throw new Error("canvas can't be undefined"); - } - this.options = options; - this._canvas = canvas; - this._ctx = canvas.getContext('2d'); - this._sprites = []; // sprites per layer - this._shader = null; - this.setCartoCSS(this.options.cartocss || DEFAULT_CARTOCSS); - this.TILE_SIZE = 256; - } - - PointRenderer.prototype = { - - setCanvas: function(canvas) { - this._canvas = canvas; - this._ctx = canvas.getContext('2d'); - }, - - // - // sets the cartocss style to render stuff - // - setCartoCSS: function(cartocss) { - // clean sprites - this.setShader(new carto.RendererJS().render(cartocss)); - }, - - setShader: function(shader) { - // clean sprites - this._sprites = []; - this._shader = shader; - }, - - clearSpriteCache: function() { - this._sprites = []; - }, - - // - // generate sprite based on cartocss style - // - generateSprite: function(shader, value, shaderVars) { - var prof = Profiler.metric('torque.renderer.point.generateSprite').start(); - var st = shader.getStyle('canvas-2d', { - value: value - }, shaderVars); - - var pointSize = st['point-radius']; - if (!pointSize) { - return null; - } - - if (st.fillOpacity === 0 && !st.strokeOpacity) { - return null; - } - - var canvas = document.createElement('canvas'); - - // take into account the exterior ring to calculate the size - var canvasSize = (st.lineWidth || 0) + pointSize*2; - var ctx = canvas.getContext('2d'); - var w = ctx.width = canvas.width = ctx.height = canvas.height = Math.ceil(canvasSize); - ctx.translate(w/2, w/2); - - if(st['point-file'] || st['marker-file']) { - torque.cartocss.renderSprite(ctx, st); - } else { - var mt = st['marker-type']; - if (mt && mt === 'rectangle') { - torque.cartocss.renderRectangle(ctx, st); - } else { - torque.cartocss.renderPoint(ctx, st); - } - } - prof.end(true); - if (torque.flags.sprites_to_images) { - var i = new Image(); - i.src = canvas.toDataURL(); - return i; - } - return canvas; - }, - - // - // renders all the layers (and frames for each layer) from cartocss - // - renderTile: function(tile, key) { - var prof = Profiler.metric('torque.renderer.point.renderLayers').start(); - var layers = this._shader.getLayers(); - for(var i = 0, n = layers.length; i < n; ++i ) { - var layer = layers[i]; - if (layer.name() !== "Map") { - var sprites = this._sprites[i] || (this._sprites[i] = {}); - // frames for each layer - for(var fr = 0; fr < layer.frames().length; ++fr) { - var frame = layer.frames()[fr]; - var fr_sprites = sprites[frame] || (sprites[frame] = []); - this._renderTile(tile, key - frame, frame, fr_sprites, layer); - } - } - } - prof.end(true); - }, - - // - // renders a tile in the canvas for key defined in - // the torque tile - // - _renderTile: function(tile, key, frame_offset, sprites, shader, shaderVars) { - if(!this._canvas) return; - - var prof = Profiler.metric('torque.renderer.point.renderTile').start(); - var ctx = this._ctx; - var blendMode = shader.eval('comp-op') || this.options.blendmode; - if(blendMode) { - ctx.globalCompositeOperation = blendMode; - } - if (this.options.cumulative && key > tile.maxDate) { - //TODO: precache because this tile is not going to change - key = tile.maxDate; - } - var tileMax = this.options.resolution * (this.TILE_SIZE/this.options.resolution - 1) - var activePixels = tile.timeCount[key]; - if(activePixels) { - var pixelIndex = tile.timeIndex[key]; - for(var p = 0; p < activePixels; ++p) { - var posIdx = tile.renderDataPos[pixelIndex + p]; - var c = tile.renderData[pixelIndex + p]; - if(c) { - var sp = sprites[c]; - if(sp === undefined) { - sp = sprites[c] = this.generateSprite(shader, c, _.extend({ zoom: tile.z, 'frame-offset': frame_offset }, shaderVars)); - } - if (sp) { - var x = tile.x[posIdx]- (sp.width >> 1); - var y = tileMax - tile.y[posIdx]; // flip mercator - ctx.drawImage(sp, x, y - (sp.height >> 1)); - } - } - } - } - prof.end(true); - }, - - setBlendMode: function(b) { - this.options.blendmode = b; - }, - - /** - * get active points for a step in active zoom - * returns a list of bounding boxes [[sw, ne] , [], []] where ne is a {lat: .., lon: ...} obj - * empty list if there is no active pixels - */ - getActivePointsBBox: function(tile, step) { - var positions = []; - var mercator = new torque.Mercator(); - - var tileMax = this.options.resolution * (this.TILE_SIZE/this.options.resolution - 1); - //this.renderer.renderTile(tile, this.key, pos.x, pos.y); - var activePixels = tile.timeCount[step]; - var pixelIndex = tile.timeIndex[step]; - for(var p = 0; p < activePixels; ++p) { - var posIdx = tile.renderDataPos[pixelIndex + p]; - var c = tile.renderData[pixelIndex + p]; - if (c) { - var x = tile.x[posIdx]; - var y = tileMax - tile.y[posIdx]; // flip mercator - positions.push(mercator.tilePixelBBox( - tile.coord.x, - tile.coord.y, - tile.coord.z, - x, y - )); - } - } - return positions; - }, - - // return the value for x, y (tile coordinates) - // null for no value - getValueFor: function(tile, step, px, py) { - var mercator = new torque.Mercator(); - var res = this.options.resolution; - var res2 = res >> 1; - - var tileMax = this.options.resolution * (this.TILE_SIZE/this.options.resolution - 1); - //this.renderer.renderTile(tile, this.key, pos.x, pos.y); - var activePixels = tile.timeCount[step]; - var pixelIndex = tile.timeIndex[step]; - for(var p = 0; p < activePixels; ++p) { - var posIdx = tile.renderDataPos[pixelIndex + p]; - var c = tile.renderData[pixelIndex + p]; - if (c) { - var x = tile.x[posIdx]; - var y = tileMax - tile.y[posIdx]; - var dx = px + res2 - x; - var dy = py + res2 - y; - if (dx >= 0 && dx < res && dy >= 0 && dy < res) { - return { - value: c, - bbox: mercator.tilePixelBBox( - tile.coord.x, - tile.coord.y, - tile.coord.z, - x - res2, y - res2, res - ) - } - } - } - } - return null; - } - - }; - - - // exports public api - exports.torque.renderer.Point = PointRenderer; - -})(typeof exports === "undefined" ? this : exports); -(function(exports) { - exports.torque = exports.torque || {}; - exports.torque.renderer = exports.torque.renderer || {}; - - var DEFAULT_CARTOCSS = [ - '#layer {', - ' polygon-fill: #FFFF00;', - ' [value > 10] { polygon-fill: #FFFF00; }', - ' [value > 100] { polygon-fill: #FFCC00; }', - ' [value > 1000] { polygon-fill: #FE9929; }', - ' [value > 10000] { polygon-fill: #FF6600; }', - ' [value > 100000] { polygon-fill: #FF3300; }', - '}' - ].join('\n'); - - var TAU = Math.PI * 2; - - // - // this renderer just render points depending of the value - // - function RectanbleRenderer(canvas, options) { - this.options = options; - carto.tree.Reference.set(torque['torque-reference']); - this.setCanvas(canvas); - this.setCartoCSS(this.options.cartocss || DEFAULT_CARTOCSS); - } - - RectanbleRenderer.prototype = { - - // - // sets the cartocss style to render stuff - // - setCartoCSS: function(cartocss) { - this._cartoCssStyle = new carto.RendererJS().render(cartocss); - if(this._cartoCssStyle.getLayers().length > 1) { - throw new Error("only one CartoCSS layer is supported"); - } - this._shader = this._cartoCssStyle.getLayers()[0].shader; - }, - - setCanvas: function(canvas) { - if(!canvas) return; - this._canvas = canvas; - this._ctx = canvas.getContext('2d'); - }, - - accumulate: function(tile, keys) { - var prof = Profiler.metric('RectangleRender:accumulate').start(); - var x, y, posIdx, p, k, key, activePixels, pixelIndex; - var res = this.options.resolution; - var s = 256/res; - var accum = new Float32Array(s*s); - - if(typeof(keys) !== 'object') { - keys = [keys]; - } - - for(k = 0; k < keys.length; ++k) { - key = keys[k]; - activePixels = tile.timeCount[key]; - if(activePixels) { - pixelIndex = tile.timeIndex[key]; - for(p = 0; p < activePixels; ++p) { - posIdx = tile.renderDataPos[pixelIndex + p]; - x = tile.x[posIdx]/res; - y = tile.y[posIdx]/res; - accum[x*s + y] += tile.renderData[pixelIndex + p]; - } - } - } - - prof.end(); - return accum; - }, - - renderTileAccum: function(accum, px, py) { - var prof = Profiler.metric('RectangleRender:renderTileAccum').start(); - var color, x, y, alpha; - var res = this.options.resolution; - var ctx = this._ctx; - var s = (256/res) | 0; - var s2 = s*s; - var colors = this._colors; - if(this.options.blendmode) { - ctx.globalCompositeOperation = this.options.blendmode; - } - var polygon_alpha = this._shader['polygon-opacity'] || function() { return 1.0; }; - for(var i = 0; i < s2; ++i) { - var xy = i; - var value = accum[i]; - if(value) { - x = (xy/s) | 0; - y = xy % s; - // by-pass the style generation for improving performance - color = this._shader['polygon-fill']({ value: value }, { zoom: 0 }); - ctx.fillStyle = color; - //TODO: each function should have a default value for each - //property defined in the cartocss - alpha = polygon_alpha({ value: value }, { zoom: 0 }); - if(alpha === null) { - alpha = 1.0; - } - ctx.globalAlpha = alpha; - ctx.fillRect(x * res, 256 - res - y * res, res, res); - } - } - prof.end(); - }, - - // - // renders a tile in the canvas for key defined in - // the torque tile - // - renderTile: function(tile, key, px, py) { - if(!this._canvas) return; - - var res = this.options.resolution; - - //var prof = Profiler.get('render').start(); - var ctx = this._ctx; - var colors = this._colors; - var activepixels = tile.timeCount[key]; - if(activepixels) { - var w = this._canvas.width; - var h = this._canvas.height; - //var imageData = ctx.getImageData(0, 0, w, h); - //var pixels = imageData.data; - var pixelIndex = tile.timeIndex[key]; - for(var p = 0; p < activePixels; ++p) { - var posIdx = tile.renderDataPos[pixelIndex + p]; - var c = tile.renderData[pixelIndex + p]; - if(c) { - var color = colors[Math.min(c, colors.length - 1)]; - var x = tile.x[posIdx];// + px; - var y = tile.y[posIdx]; //+ py; - - ctx.fillStyle = color; - ctx.fillRect(x, y, res, res); - /* - - for(var xx = 0; xx < res; ++xx) { - for(var yy = 0; yy < res; ++yy) { - var idx = 4*((x+xx) + w*(y + yy)); - pixels[idx + 0] = color[0]; - pixels[idx + 1] = color[1]; - pixels[idx + 2] = color[2]; - pixels[idx + 3] = color[3]; - } - } - */ - } - } - //ctx.putImageData(imageData, 0, 0); - } - //prof.end(); - } - }; - - - // exports public api - exports.torque.renderer.Rectangle = RectanbleRenderer; - -})(typeof exports === "undefined" ? this : exports); -/** - * @license - * Copyright 2013 Google Inc. All Rights Reserved. - * - * 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. - */ - -/** - * @fileoverview Extends OverlayView to provide a canvas "Layer". - * @author Brendan Kenny - */ - -/** - * A map layer that provides a canvas over the slippy map and a callback - * system for efficient animation. Requires canvas and CSS 2D transform - * support. - * @constructor - * @extends google.maps.OverlayView - * @param {CanvasLayerOptions=} opt_options Options to set in this CanvasLayer. - */ - -if(typeof(google) !== 'undefined' && typeof(google.maps) !== 'undefined') { -function CanvasLayer(opt_options) { - /** - * If true, canvas is in a map pane and the OverlayView is fully functional. - * See google.maps.OverlayView.onAdd for more information. - * @type {boolean} - * @private - */ - this.isAdded_ = false; - - /** - * If true, each update will immediately schedule the next. - * @type {boolean} - * @private - */ - this.isAnimated_ = false; - - /** - * The name of the MapPane in which this layer will be displayed. - * @type {string} - * @private - */ - this.paneName_ = CanvasLayer.DEFAULT_PANE_NAME_; - - /** - * A user-supplied function called whenever an update is required. Null or - * undefined if a callback is not provided. - * @type {?function=} - * @private - */ - this.updateHandler_ = null; - - /** - * A user-supplied function called whenever an update is required and the - * map has been resized since the last update. Null or undefined if a - * callback is not provided. - * @type {?function} - * @private - */ - this.resizeHandler_ = null; - - /** - * The LatLng coordinate of the top left of the current view of the map. Will - * be null when this.isAdded_ is false. - * @type {google.maps.LatLng} - * @private - */ - this.topLeft_ = null; - - /** - * The map-pan event listener. Will be null when this.isAdded_ is false. Will - * be null when this.isAdded_ is false. - * @type {?function} - * @private - */ - this.centerListener_ = null; - - /** - * The map-resize event listener. Will be null when this.isAdded_ is false. - * @type {?function} - * @private - */ - this.resizeListener_ = null; - - /** - * If true, the map size has changed and this.resizeHandler_ must be called - * on the next update. - * @type {boolean} - * @private - */ - this.needsResize_ = true; - - /** - * A browser-defined id for the currently requested callback. Null when no - * callback is queued. - * @type {?number} - * @private - */ - this.requestAnimationFrameId_ = null; - - var canvas = document.createElement('canvas'); - canvas.style.position = 'absolute'; - canvas.style.top = 0; - canvas.style.left = 0; - canvas.style.pointerEvents = 'none'; - - /** - * The canvas element. - * @type {!HTMLCanvasElement} - */ - this.canvas = canvas; - - /** - * Simple bind for functions with no args for bind-less browsers (Safari). - * @param {Object} thisArg The this value used for the target function. - * @param {function} func The function to be bound. - */ - function simpleBindShim(thisArg, func) { - return function() { func.apply(thisArg); }; - } - - /** - * A reference to this.repositionCanvas_ with this bound as its this value. - * @type {function} - * @private - */ - this.repositionFunction_ = simpleBindShim(this, this.repositionCanvas_); - - /** - * A reference to this.resize_ with this bound as its this value. - * @type {function} - * @private - */ - this.resizeFunction_ = simpleBindShim(this, this.resize_); - - /** - * A reference to this.update_ with this bound as its this value. - * @type {function} - * @private - */ - this.requestUpdateFunction_ = simpleBindShim(this, this.update_); - - // set provided options, if any - if (opt_options) { - this.setOptions(opt_options); - } -} - -CanvasLayer.prototype = new google.maps.OverlayView(); - -/** - * The default MapPane to contain the canvas. - * @type {string} - * @const - * @private - */ -CanvasLayer.DEFAULT_PANE_NAME_ = 'overlayLayer'; - -/** - * Transform CSS property name, with vendor prefix if required. If browser - * does not support transforms, property will be ignored. - * @type {string} - * @const - * @private - */ -CanvasLayer.CSS_TRANSFORM_ = (function() { - var div = document.createElement('div'); - var transformProps = [ - 'transform', - 'WebkitTransform', - 'MozTransform', - 'OTransform', - 'msTransform' - ]; - for (var i = 0; i < transformProps.length; i++) { - var prop = transformProps[i]; - if (div.style[prop] !== undefined) { - return prop; - } - } - - // return unprefixed version by default - return transformProps[0]; -})(); - -/** - * The requestAnimationFrame function, with vendor-prefixed or setTimeout-based - * fallbacks. MUST be called with window as thisArg. - * @type {function} - * @param {function} callback The function to add to the frame request queue. - * @return {number} The browser-defined id for the requested callback. - * @private - */ -CanvasLayer.prototype.requestAnimFrame_ = - window.requestAnimationFrame || - window.webkitRequestAnimationFrame || - window.mozRequestAnimationFrame || - window.oRequestAnimationFrame || - window.msRequestAnimationFrame || - function(callback) { - return window.setTimeout(callback, 1000 / 60); - }; - -/** - * The cancelAnimationFrame function, with vendor-prefixed fallback. Does not - * fall back to clearTimeout as some platforms implement requestAnimationFrame - * but not cancelAnimationFrame, and the cost is an extra frame on onRemove. - * MUST be called with window as thisArg. - * @type {function} - * @param {number=} requestId The id of the frame request to cancel. - * @private - */ -CanvasLayer.prototype.cancelAnimFrame_ = - window.cancelAnimationFrame || - window.webkitCancelAnimationFrame || - window.mozCancelAnimationFrame || - window.oCancelAnimationFrame || - window.msCancelAnimationFrame || - function(requestId) {}; - -/** - * Sets any options provided. See CanvasLayerOptions for more information. - * @param {CanvasLayerOptions} options The options to set. - */ -CanvasLayer.prototype.setOptions = function(options) { - if (options.animate !== undefined) { - this.setAnimate(options.animate); - } - - if (options.paneName !== undefined) { - this.setPane(options.paneName); - } - - if (options.updateHandler !== undefined) { - this.setUpdateHandler(options.updateHandler); - } - - if (options.resizeHandler !== undefined) { - this.setResizeHandler(options.resizeHandler); - } - - if(options.readyHandler) { - this.readyHandler = options.readyHandler; - } - -}; - -/** - * Set the animated state of the layer. If true, updateHandler will be called - * repeatedly, once per frame. If false, updateHandler will only be called when - * a map property changes that could require the canvas content to be redrawn. - * @param {boolean} animate Whether the canvas is animated. - */ -CanvasLayer.prototype.setAnimate = function(animate) { - this.isAnimated_ = !!animate; - - if (this.isAnimated_) { - this.scheduleUpdate(); - } -}; - -/** - * @return {boolean} Whether the canvas is animated. - */ -CanvasLayer.prototype.isAnimated = function() { - return this.isAnimated_; -}; - -/** - * Set the MapPane in which this layer will be displayed, by name. See - * {@code google.maps.MapPanes} for the panes available. - * @param {string} paneName The name of the desired MapPane. - */ -CanvasLayer.prototype.setPaneName = function(paneName) { - this.paneName_ = paneName; - - this.setPane_(); -}; - -/** - * @return {string} The name of the current container pane. - */ -CanvasLayer.prototype.getPaneName = function() { - return this.paneName_; -}; - -/** - * Adds the canvas to the specified container pane. Since this is guaranteed to - * execute only after onAdd is called, this is when paneName's existence is - * checked (and an error is thrown if it doesn't exist). - * @private - */ -CanvasLayer.prototype.setPane_ = function() { - if (!this.isAdded_) { - return; - } - - // onAdd has been called, so panes can be used - var panes = this.getPanes(); - if (!panes[this.paneName_]) { - throw new Error('"' + this.paneName_ + '" is not a valid MapPane name.'); - } - - panes[this.paneName_].appendChild(this.canvas); -}; - -/** - * Set a function that will be called whenever the parent map and the overlay's - * canvas have been resized. If opt_resizeHandler is null or unspecified, any - * existing callback is removed. - * @param {?function=} opt_resizeHandler The resize callback function. - */ -CanvasLayer.prototype.setResizeHandler = function(opt_resizeHandler) { - this.resizeHandler_ = opt_resizeHandler; -}; - -/** - * Set a function that will be called when a repaint of the canvas is required. - * If opt_updateHandler is null or unspecified, any existing callback is - * removed. - * @param {?function=} opt_updateHandler The update callback function. - */ -CanvasLayer.prototype.setUpdateHandler = function(opt_updateHandler) { - this.updateHandler_ = opt_updateHandler; -}; - -/** - * @inheritDoc - */ -CanvasLayer.prototype.onAdd = function() { - if (this.isAdded_) { - return; - } - - this.isAdded_ = true; - this.setPane_(); - - this.resizeListener_ = google.maps.event.addListener(this.getMap(), - 'resize', this.resizeFunction_); - this.centerListener_ = google.maps.event.addListener(this.getMap(), - 'center_changed', this.repositionFunction_); - - this.resize_(); - this.repositionCanvas_(); - this.readyHandler && this.readyHandler(); -}; - -/** - * @inheritDoc - */ -CanvasLayer.prototype.onRemove = function() { - if (!this.isAdded_) { - return; - } - - this.isAdded_ = false; - this.topLeft_ = null; - - // remove canvas and listeners for pan and resize from map - this.canvas.parentElement.removeChild(this.canvas); - if (this.centerListener_) { - google.maps.event.removeListener(this.centerListener_); - this.centerListener_ = null; - } - if (this.resizeListener_) { - google.maps.event.removeListener(this.resizeListener_); - this.resizeListener_ = null; - } - - // cease canvas update callbacks - if (this.requestAnimationFrameId_) { - this.cancelAnimFrame_.call(window, this.requestAnimationFrameId_); - this.requestAnimationFrameId_ = null; - } -}; - -/** - * The internal callback for resize events that resizes the canvas to keep the - * map properly covered. - * @private - */ -CanvasLayer.prototype.resize_ = function() { - // TODO(bckenny): it's common to use a smaller canvas but use CSS to scale - // what is drawn by the browser to save on fill rate. Add an option to do - // this. - - if (!this.isAdded_) { - return; - } - - var map = this.getMap(); - var width = map.getDiv().offsetWidth; - var height = map.getDiv().offsetHeight; - var oldWidth = this.canvas.width; - var oldHeight = this.canvas.height; - - // resizing may allocate a new back buffer, so do so conservatively - if (oldWidth !== width || oldHeight !== height) { - this.canvas.width = width; - this.canvas.height = height; - this.canvas.style.width = width + 'px'; - this.canvas.style.height = height + 'px'; - - this.needsResize_ = true; - this.scheduleUpdate(); - } -}; - -/** - * @inheritDoc - */ -CanvasLayer.prototype.draw = function() { - this.repositionCanvas_(); -}; - -/** - * Internal callback for map view changes. Since the Maps API moves the overlay - * along with the map, this function calculates the opposite translation to - * keep the canvas in place. - * @private - */ -CanvasLayer.prototype.repositionCanvas_ = function() { - // TODO(bckenny): *should* only be executed on RAF, but in current browsers - // this causes noticeable hitches in map and overlay relative - // positioning. - - var bounds = this.getMap().getBounds(); - this.topLeft_ = new google.maps.LatLng(bounds.getNorthEast().lat(), - bounds.getSouthWest().lng()); - - // canvas position relative to draggable map's conatainer depends on - // overlayView's projection, not the map's - var projection = this.getProjection(); - var divTopLeft = projection.fromLatLngToDivPixel(this.topLeft_); - - // when the zoom level is low, more than one map can be shown in the screen - // so the canvas should be attach to the map with more are in the screen - var mapSize = (1 << this.getMap().getZoom())*256; - if (Math.abs(divTopLeft.x) > mapSize) { - divTopLeft.x -= mapSize; - } - this.canvas.style[CanvasLayer.CSS_TRANSFORM_] = 'translate(' + - Math.round(divTopLeft.x) + 'px,' + Math.round(divTopLeft.y) + 'px)'; - - this.scheduleUpdate(); -}; - -/** - * Internal callback that serves as main animation scheduler via - * requestAnimationFrame. Calls resize and update callbacks if set, and - * schedules the next frame if overlay is animated. - * @private - */ -CanvasLayer.prototype.update_ = function() { - this.requestAnimationFrameId_ = null; - - if (!this.isAdded_) { - return; - } - - if (this.isAnimated_) { - this.scheduleUpdate(); - } - - if (this.needsResize_ && this.resizeHandler_) { - this.needsResize_ = false; - this.resizeHandler_(); - } - - if (this.updateHandler_) { - this.updateHandler_(); - } -}; - -/** - * A convenience method to get the current LatLng coordinate of the top left of - * the current view of the map. - * @return {google.maps.LatLng} The top left coordinate. - */ -CanvasLayer.prototype.getTopLeft = function() { - return this.topLeft_; -}; - -/** - * Schedule a requestAnimationFrame callback to updateHandler. If one is - * already scheduled, there is no effect. - */ -CanvasLayer.prototype.scheduleUpdate = function() { - if (this.isAdded_ && !this.requestAnimationFrameId_) { - this.requestAnimationFrameId_ = - this.requestAnimFrame_.call(window, this.requestUpdateFunction_); - } -}; -} -/* - ==================== - canvas setup for drawing tiles - ==================== - */ - -if(typeof(google) !== 'undefined' && typeof(google.maps) !== 'undefined') { - -function CanvasTileLayer(canvas_setup, render) { - this.tileSize = new google.maps.Size(256, 256); - this.maxZoom = 19; - this.name = "Tile #s"; - this.alt = "Canvas tile layer"; - this.tiles = {}; - this.canvas_setup = canvas_setup; - this.render = render; - if (!render) { - this.render = canvas_setup; - } -} - - -// create a tile with a canvas element -CanvasTileLayer.prototype.create_tile_canvas = function (coord, zoom, ownerDocument) { - - // create canvas and reset style - var canvas = ownerDocument.createElement('canvas'); - var hit_canvas = ownerDocument.createElement('canvas'); - canvas.style.border = hit_canvas.style.border = "none"; - canvas.style.margin = hit_canvas.style.margin = "0"; - canvas.style.padding = hit_canvas.style.padding = "0"; - - // prepare canvas and context sizes - var ctx = canvas.getContext('2d'); - ctx.width = canvas.width = this.tileSize.width; - ctx.height = canvas.height = this.tileSize.height; - - var hit_ctx = hit_canvas.getContext('2d'); - hit_canvas.width = hit_ctx.width = this.tileSize.width; - hit_canvas.height = hit_ctx.height = this.tileSize.height; - - //set unique id - var tile_id = coord.x + '_' + coord.y + '_' + zoom; - - canvas.setAttribute('id', tile_id); - hit_canvas.setAttribute('id', tile_id); - - if (tile_id in this.tiles) - delete this.tiles[tile_id]; - - this.tiles[tile_id] = {canvas:canvas, ctx:ctx, hit_canvas:hit_canvas, hit_ctx:hit_ctx, coord:coord, zoom:zoom, primitives:null}; - - // custom setup - //if (tile_id == '19295_24654_16'){ - if (this.canvas_setup) - this.canvas_setup(this.tiles[tile_id], coord, zoom); - //} - return canvas; - -} - - -CanvasTileLayer.prototype.each = function (callback) { - for (var t in this.tiles) { - var tile = this.tiles[t]; - callback(tile); - } -} - -CanvasTileLayer.prototype.recreate = function () { - for (var t in this.tiles) { - var tile = this.tiles[t]; - this.canvas_setup(tile, tile.coord, tile.zoom); - } -}; - -CanvasTileLayer.prototype.redraw_tile = function (tile) { - this.render(tile, tile.coord, tile.zoom); -}; - -CanvasTileLayer.prototype.redraw = function () { - for (var t in this.tiles) { - var tile = this.tiles[t]; - this.render(tile, tile.coord, tile.zoom); - } -}; - -// could be called directly... -CanvasTileLayer.prototype.getTile = function (coord, zoom, ownerDocument) { - return this.create_tile_canvas(coord, zoom, ownerDocument); -}; - -CanvasTileLayer.prototype.releaseTile = function (tile) { - var id = tile.getAttribute('id'); - delete this.tiles[id]; -}; - -} -(function(exports) { - -if(typeof(google) === 'undefined' || typeof(google.maps) === 'undefined') return; - -function GMapsTileLoader() { -} - - -GMapsTileLoader.prototype = { - - _initTileLoader: function(map, projection) { - this._map = map; - this._projection = projection; - this._tiles = {}; - this._tilesLoading = {}; - this._tilesToLoad = 0; - this._updateTiles = this._updateTiles.bind(this); - this._listeners = []; - this._listeners.push( - google.maps.event.addListener(this._map, 'dragend', this._updateTiles), - google.maps.event.addListener(this._map, 'zoom_changed', this._updateTiles) - ); - this.tileSize = 256; - this._updateTiles(); - }, - - _removeTileLoader: function() { - for(var i in this._listeners) { - google.maps.event.removeListener(this._listeners[i]); - } - this._removeTiles(); - }, - - _removeTiles: function () { - for (var key in this._tiles) { - this._removeTile(key); - } - }, - - _reloadTiles: function() { - this._removeTiles(); - this._updateTiles(); - }, - - _updateTiles: function () { - - if (!this._map) { return; } - - var bounds = this._map.getBounds(); - var zoom = this._map.getZoom(); - var tileSize = this.tileSize; - var mzoom = (1 << zoom); - - var topLeft = new google.maps.LatLng( - bounds.getNorthEast().lat(), - bounds.getSouthWest().lng() - ); - - var bottomRigth = new google.maps.LatLng( - bounds.getSouthWest().lat(), - bounds.getNorthEast().lng() - ); - - - this._projection = this._map.getProjection(); - var divTopLeft = this._projection.fromLatLngToPoint(topLeft); - var divBottomRight = this._projection.fromLatLngToPoint(bottomRigth); - - - var nwTilePoint = new google.maps.Point( - Math.floor(divTopLeft.x*mzoom / tileSize), - Math.floor(divTopLeft.y*mzoom / tileSize)), - seTilePoint = new google.maps.Point( - Math.floor(divBottomRight.x*mzoom / tileSize), - Math.floor(divBottomRight.y*mzoom / tileSize)); - - - this._addTilesFromCenterOut(nwTilePoint, seTilePoint); - this._removeOtherTiles(nwTilePoint, seTilePoint); - }, - - _removeOtherTiles: function (nwTilePoint, seTilePoint) { - var kArr, x, y, key; - - var zoom = this._map.getZoom(); - for (key in this._tiles) { - if (this._tiles.hasOwnProperty(key)) { - kArr = key.split(':'); - x = parseInt(kArr[0], 10); - y = parseInt(kArr[1], 10); - z = parseInt(kArr[2], 10); - - // remove tile if it's out of bounds - if (z !== zoom || x < nwTilePoint.x || x > seTilePoint.x || y < nwTilePoint.y || y > seTilePoint.y) { - this._removeTile(key); - } - } - } - }, - - _removeTile: function (key) { - this.onTileRemoved && this.onTileRemoved(this._tiles[key]); - delete this._tiles[key]; - delete this._tilesLoading[key]; - }, - - _tileKey: function(tilePoint) { - return tilePoint.x + ':' + tilePoint.y + ':' + tilePoint.zoom; - }, - - _tileShouldBeLoaded: function (tilePoint) { - var k = this._tileKey(tilePoint); - return !(k in this._tiles) && !(k in this._tilesLoading); - }, - - _tileLoaded: function(tilePoint, tileData) { - this._tilesToLoad--; - var k = tilePoint.x + ':' + tilePoint.y + ':' + tilePoint.zoom - this._tiles[k] = tileData; - delete this._tilesLoading[k]; - if(this._tilesToLoad === 0) { - this.onTilesLoaded && this.onTilesLoaded(); - } - }, - - getTilePos: function (tilePoint) { - var limit = (1 << this._map.getZoom()); - // wrap tile - tilePoint = { - x: ((tilePoint.x % limit) + limit) % limit, - y: tilePoint.y - }; - - tilePoint = new google.maps.Point( - tilePoint.x * this.tileSize, - tilePoint.y * this.tileSize - ); - - var bounds = this._map.getBounds(); - var topLeft = new google.maps.LatLng( - bounds.getNorthEast().lat(), - bounds.getSouthWest().lng() - ); - - var divTopLeft = this._map.getProjection().fromLatLngToPoint(topLeft); - zoom = (1 << this._map.getZoom()); - divTopLeft.x = divTopLeft.x * zoom; - divTopLeft.y = divTopLeft.y * zoom; - - return new google.maps.Point( - tilePoint.x - divTopLeft.x, - tilePoint.y - divTopLeft.y - ); - }, - - _addTilesFromCenterOut: function (nwTilePoint, seTilePoint) { - var queue = [], - center = new google.maps.Point( - (nwTilePoint.x + seTilePoint.x) * 0.5, - (nwTilePoint.y + seTilePoint.y) * 0.5 - ), - zoom = this._map.getZoom(); - - var j, i, point; - - for (j = nwTilePoint.y; j <= seTilePoint.y; j++) { - for (i = nwTilePoint.x; i <= seTilePoint.x; i++) { - point = new google.maps.Point (i, j); - point.zoom = zoom; - - if (this._tileShouldBeLoaded(point)) { - queue.push(point); - } - } - } - - var tilesToLoad = queue.length; - - if (tilesToLoad === 0) { return; } - - function distanceToCenterSq(point) { - var dx = point.x - center.x; - var dy = point.y - center.y; - return dx * dx + dy * dy; - } - - // load tiles in order of their distance to center - queue.sort(function (a, b) { - return distanceToCenterSq(a) - distanceToCenterSq(b); - }); - - this._tilesToLoad += tilesToLoad; - - for (i = 0; i < tilesToLoad; i++) { - var t = queue[i]; - var k = this._tileKey(t); - this._tilesLoading[k] = t; - // events - if (this.onTileAdded) { - this.onTileAdded(t); - } - } - - this.onTilesLoading && this.onTilesLoading(); - } - -} - -torque.GMapsTileLoader = GMapsTileLoader; - -})(typeof exports === "undefined" ? this : exports); -(function(exports) { - -if(typeof(google) === 'undefined' || typeof(google.maps) === 'undefined') return; - -function GMapsTorqueLayer(options) { - var self = this; - if (!torque.isBrowserSupported()) { - throw new Error("browser is not supported by torque"); - } - this.key = 0; - this.shader = null; - this.ready = false; - this.options = _.extend({}, options); - _.defaults(this.options, { - provider: 'windshaft', - renderer: 'point', - resolution: 2, - steps: 100, - visible: true - }); - if (options.cartocss) { - _.extend(this.options, - torque.common.TorqueLayer.optionsFromCartoCSS(options.cartocss)); - } - - this.hidden = !this.options.visible; - - this.animator = new torque.Animator(function(time) { - var k = time | 0; - if(self.key !== k) { - self.setKey(k); - } - }, torque.clone(this.options)); - - this.play = this.animator.start.bind(this.animator); - this.stop = this.animator.stop.bind(this.animator); - this.pause = this.animator.pause.bind(this.animator); - this.toggle = this.animator.toggle.bind(this.animator); - this.setDuration = this.animator.duration.bind(this.animator); - this.isRunning = this.animator.isRunning.bind(this.animator); - - - CanvasLayer.call(this, { - map: this.options.map, - //resizeHandler: this.redraw, - animate: false, - updateHandler: this.render, - readyHandler: this.initialize - }); - -} - -/** - * torque layer - */ -GMapsTorqueLayer.prototype = _.extend({}, - CanvasLayer.prototype, - torque.GMapsTileLoader.prototype, - torque.Event, - { - - providers: { - 'sql_api': torque.providers.json, - 'url_template': torque.providers.jsonarray, - 'windshaft': torque.providers.windshaft - }, - - renderers: { - 'point': torque.renderer.Point, - 'pixel': torque.renderer.Rectangle - }, - - initialize: function() { - var self = this; - - this.onTileAdded = this.onTileAdded.bind(this); - - this.options.ready = function() { - self.fire("change:bounds", { - bounds: self.provider.getBounds() - }); - self.animator.steps(self.provider.getSteps()); - self.animator.rescale(); - self.fire('change:steps', { - steps: self.provider.getSteps() - }); - self.setKey(self.key); - }; - - this.provider = new this.providers[this.options.provider](this.options); - this.renderer = new this.renderers[this.options.renderer](this.getCanvas(), this.options); - - // this listener should be before tile loader - this._cacheListener = google.maps.event.addListener(this.options.map, 'zoom_changed', function() { - self.renderer && self.renderer.clearSpriteCache(); - }); - - this._initTileLoader(this.options.map, this.getProjection()); - - if (this.shader) { - this.renderer.setShader(this.shader); - } - - }, - - hide: function() { - if(this.hidden) return this; - this.pause(); - this.clear(); - this.hidden = true; - return this; - }, - - show: function() { - if(!this.hidden) return this; - this.hidden = false; - this.play(); - return this; - }, - - setSQL: function(sql) { - if (!this.provider || !this.provider.setSQL) { - throw new Error("this provider does not support SQL"); - } - this.provider.setSQL(sql); - this._reloadTiles(); - return this; - }, - - setBlendMode: function(_) { - this.renderer && this.renderer.setBlendMode(_); - this.redraw(); - }, - - setSteps: function(steps) { - this.provider && this.provider.setSteps(steps); - this.animator && this.animator.steps(steps); - this._reloadTiles(); - }, - - setColumn: function(column, isTime) { - this.provider && this.provider.setColumn(column, isTime); - this._reloadTiles(); - }, - - getTimeBounds: function() { - return this.provider && this.provider.getKeySpan(); - }, - - getCanvas: function() { - return this.canvas; - }, - - // for each tile shown on the map request the data - onTileAdded: function(t) { - var self = this; - this.provider.getTileData(t, t.zoom, function(tileData) { - // don't load tiles that are not being shown - if (t.zoom !== self.options.map.getZoom()) return; - self._tileLoaded(t, tileData); - if (tileData) { - self.redraw(); - } - }); - }, - - clear: function() { - var canvas = this.canvas; - canvas.width = canvas.width; - }, - - /** - * render the selectef key - * don't call this function directly, it's called by - * requestAnimationFrame. Use redraw to refresh it - */ - render: function() { - if(this.hidden) return; - var t, tile, pos; - var canvas = this.canvas; - canvas.width = canvas.width; - var ctx = canvas.getContext('2d'); - - // renders only a "frame" - for(t in this._tiles) { - tile = this._tiles[t]; - if (tile) { - pos = this.getTilePos(tile.coord); - ctx.setTransform(1, 0, 0, 1, pos.x, pos.y); - this.renderer.renderTile(tile, this.key, pos.x, pos.y); - } - } - }, - - getActivePointsBBox: function(step) { - var positions = []; - var tileMax = this.options.resolution * (256/this.options.resolution - 1); - for(var t in this._tiles) { - var tile = this._tiles[t]; - positions = positions.concat(this.renderer.getActivePointsBBox(tile, step)); - } - return positions; - }, - - /** - * set key to be shown. If it's a single value - * it renders directly, if it's an array it renders - * accumulated - */ - setKey: function(key) { - this.key = key; - this.animator.step(key); - this.redraw(); - this.fire('change:time', { time: this.getTime(), step: this.key }); - }, - - /** - * helper function, does the same than ``setKey`` but only - * accepts scalars. - */ - setStep: function(time) { - if(time === undefined || time.length !== undefined) { - throw new Error("setTime only accept scalars"); - } - this.setKey(time); - }, - - /** - * transform from animation step to Date object - * that contains the animation time - * - * ``step`` should be between 0 and ``steps - 1`` - */ - stepToTime: function(step) { - if (!this.provider) return 0; - var times = this.provider.getKeySpan(); - var time = times.start + (times.end - times.start)*(step/this.provider.getSteps()); - return new Date(time); - }, - - getStep: function() { - return this.key; - }, - - /** - * returns the animation time defined by the data - * in the defined column. Date object - */ - getTime: function() { - return this.stepToTime(this.key); - }, - - /** - * set the cartocss for the current renderer - */ - setCartoCSS: function(cartocss) { - var shader = new carto.RendererJS().render(cartocss); - this.shader = shader; - if (this.renderer) { - this.renderer.setShader(shader); - } - - // provider options - var options = torque.common.TorqueLayer.optionsFromLayer(shader.findLayer({ name: 'Map' })); - this.provider && this.provider.setCartoCSS && this.provider.setCartoCSS(cartocss); - if(this.provider && this.provider.setOptions(options)) { - this._reloadTiles(); - } - _.extend(this.options, options); - - // animator options - if (options.animationDuration) { - this.animator.duration(options.animationDuration); - } - - this.redraw(); - return this; - }, - - redraw: function() { - this.scheduleUpdate(); - }, - - onRemove: function() { - CanvasLayer.prototype.onRemove.call(this); - this.animator.stop(); - this._removeTileLoader(); - google.maps.event.removeListener(this._cacheListener); - } - -}); - - - -function GMapsTiledTorqueLayer(options) { - this.options = _.extend({}, options); - CanvasTileLayer.call(this, this._loadTile.bind(this), this.drawTile.bind(this)); - this.initialize(options); -} - -GMapsTiledTorqueLayer.prototype = _.extend({}, CanvasTileLayer.prototype, { - - providers: { - 'sql_api': torque.providers.json, - 'url_template': torque.providers.JsonArray - }, - - renderers: { - 'point': torque.renderer.Point, - 'pixel': torque.renderer.Rectangle - }, - - initialize: function(options) { - var self = this; - this.key = 0; - - this.options.renderer = this.options.renderer || 'pixel'; - this.options.provider = this.options.provider || 'sql_api'; - - this.provider = new this.providers[this.options.provider](options); - this.renderer = new this.renderers[this.options.renderer](null, options); - - }, - - _tileLoaded: function(tile, tileData) { - tile.data = tileData; - this.drawTile(tile); - }, - - _loadTile: function(tile, coord, zoom) { - var self = this; - var limit = 1 << zoom; - // wrap tile - var wrappedCoord = { - x: ((coord.x % limit) + limit) % limit, - y: coord.y - }; - - this.provider.getTileData(wrappedCoord, zoom, function(tileData) { - self._tileLoaded(tile, tileData); - }); - }, - - drawTile: function (tile) { - var canvas = tile.canvas; - if(!tile.data) return; - canvas.width = canvas.width; - - this.renderer.setCanvas(canvas); - - var accum = this.renderer.accumulate(tile.data, this.key); - this.renderer.renderTileAccum(accum, 0, 0); - }, - - setKey: function(key) { - this.key = key; - this.redraw(); - }, - - /** - * set the cartocss for the current renderer - */ - setCartoCSS: function(cartocss) { - if (!this.renderer) throw new Error('renderer is not valid'); - return this.renderer.setCartoCSS(cartocss); - } - -}); - -exports.torque.GMapsTiledTorqueLayer = GMapsTiledTorqueLayer; -exports.torque.GMapsTorqueLayer = GMapsTorqueLayer; - -})(typeof exports === "undefined" ? this : exports); -if(typeof(L) !== 'undefined') { - -L.Mixin.TileLoader = { - - _initTileLoader: function() { - this._tiles = {} - this._tilesLoading = {}; - this._tilesToLoad = 0; - this._map.on({ - 'moveend': this._updateTiles - }, this); - this._updateTiles(); - }, - - _removeTileLoader: function() { - this._map.off({ - 'moveend': this._updateTiles - }, this); - this._removeTiles(); - }, - - _updateTiles: function () { - - if (!this._map) { return; } - - var bounds = this._map.getPixelBounds(), - zoom = this._map.getZoom(), - tileSize = this.options.tileSize; - - if (zoom > this.options.maxZoom || zoom < this.options.minZoom) { - return; - } - - var nwTilePoint = new L.Point( - Math.floor(bounds.min.x / tileSize), - Math.floor(bounds.min.y / tileSize)), - - seTilePoint = new L.Point( - Math.floor(bounds.max.x / tileSize), - Math.floor(bounds.max.y / tileSize)), - - tileBounds = new L.Bounds(nwTilePoint, seTilePoint); - - this._addTilesFromCenterOut(tileBounds); - this._removeOtherTiles(tileBounds); - }, - - _removeTiles: function (bounds) { - for (var key in this._tiles) { - this._removeTile(key); - } - }, - - _reloadTiles: function() { - this._removeTiles(); - this._updateTiles(); - }, - - _removeOtherTiles: function (bounds) { - var kArr, x, y, z, key; - var zoom = this._map.getZoom(); - - for (key in this._tiles) { - if (this._tiles.hasOwnProperty(key)) { - kArr = key.split(':'); - x = parseInt(kArr[0], 10); - y = parseInt(kArr[1], 10); - z = parseInt(kArr[2], 10); - - // remove tile if it's out of bounds - if (zoom !== z || x < bounds.min.x || x > bounds.max.x || y < bounds.min.y || y > bounds.max.y) { - this._removeTile(key); - } - } - } - }, - - _removeTile: function (key) { - this.fire('tileRemoved', this._tiles[key]); - delete this._tiles[key]; - delete this._tilesLoading[key]; - }, - - _tileKey: function(tilePoint) { - return tilePoint.x + ':' + tilePoint.y + ':' + tilePoint.zoom; - }, - - _tileShouldBeLoaded: function (tilePoint) { - var k = this._tileKey(tilePoint); - return !(k in this._tiles) && !(k in this._tilesLoading); - }, - - _tileLoaded: function(tilePoint, tileData) { - this._tilesToLoad--; - var k = tilePoint.x + ':' + tilePoint.y + ':' + tilePoint.zoom - this._tiles[k] = tileData; - delete this._tilesLoading[k]; - if(this._tilesToLoad === 0) { - this.fire("tilesLoaded"); - } - }, - - getTilePos: function (tilePoint) { - tilePoint = new L.Point(tilePoint.x, tilePoint.y); - var origin = this._map._getNewTopLeftPoint(this._map.getCenter()), - tileSize = this.options.tileSize; - - return tilePoint.multiplyBy(tileSize).subtract(origin); - }, - - _addTilesFromCenterOut: function (bounds) { - var queue = [], - center = bounds.getCenter(), - zoom = this._map.getZoom(); - - var j, i, point; - - for (j = bounds.min.y; j <= bounds.max.y; j++) { - for (i = bounds.min.x; i <= bounds.max.x; i++) { - point = new L.Point(i, j); - point.zoom = zoom; - - if (this._tileShouldBeLoaded(point)) { - queue.push(point); - } - } - } - - var tilesToLoad = queue.length; - - if (tilesToLoad === 0) { return; } - - // load tiles in order of their distance to center - queue.sort(function (a, b) { - return a.distanceTo(center) - b.distanceTo(center); - }); - - this._tilesToLoad += tilesToLoad; - - for (i = 0; i < tilesToLoad; i++) { - var t = queue[i]; - var k = this._tileKey(t); - this._tilesLoading[k] = t; - this.fire('tileAdded', t); - } - this.fire("tilesLoading"); - - } -} - -} //L defined -if(typeof(L) !== 'undefined') { -/** - * full canvas layer implementation for Leaflet - */ - -L.CanvasLayer = L.Class.extend({ - - includes: [L.Mixin.Events, L.Mixin.TileLoader], - - options: { - minZoom: 0, - maxZoom: 28, - tileSize: 256, - subdomains: 'abc', - errorTileUrl: '', - attribution: '', - zoomOffset: 0, - opacity: 1, - unloadInvisibleTiles: L.Browser.mobile, - updateWhenIdle: L.Browser.mobile, - tileLoader: false, // installs tile loading events - zoomAnimation: true - }, - - initialize: function (options) { - var self = this; - options = options || {}; - //this.project = this._project.bind(this); - this.render = this.render.bind(this); - L.Util.setOptions(this, options); - this._canvas = this._createCanvas(); - // backCanvas for zoom animation - if (this.options.zoomAnimation) { - this._backCanvas = this._createCanvas(); - } - this._ctx = this._canvas.getContext('2d'); - this.currentAnimationFrame = -1; - this.requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame || - window.webkitRequestAnimationFrame || window.msRequestAnimationFrame || function(callback) { - return window.setTimeout(callback, 1000 / 60); - }; - this.cancelAnimationFrame = window.cancelAnimationFrame || window.mozCancelAnimationFrame || - window.webkitCancelAnimationFrame || window.msCancelAnimationFrame || function(id) { clearTimeout(id); }; - }, - - _createCanvas: function() { - var canvas; - canvas = document.createElement('canvas'); - canvas.style.position = 'absolute'; - canvas.style.top = 0; - canvas.style.left = 0; - canvas.style.pointerEvents = "none"; - canvas.style.zIndex = this.options.zIndex || 0; - var className = 'leaflet-tile-container'; - if (this.options.zoomAnimation) { - className += ' leaflet-zoom-animated'; - } - canvas.setAttribute('class', className); - return canvas; - }, - - onAdd: function (map) { - this._map = map; - - // add container with the canvas to the tile pane - // the container is moved in the oposite direction of the - // map pane to keep the canvas always in (0, 0) - var tilePane = this._map._panes.tilePane; - var _container = L.DomUtil.create('div', 'leaflet-layer'); - _container.appendChild(this._canvas); - if (this.options.zoomAnimation) { - _container.appendChild(this._backCanvas); - this._backCanvas.style.display = 'none'; - } - tilePane.appendChild(_container); - - this._container = _container; - - // hack: listen to predrag event launched by dragging to - // set container in position (0, 0) in screen coordinates - map.dragging._draggable.on('predrag', function() { - var d = map.dragging._draggable; - L.DomUtil.setPosition(this._canvas, { x: -d._newPos.x, y: -d._newPos.y }); - }, this); - - map.on({ 'viewreset': this._reset }, this); - map.on('move', this.render, this); - map.on('resize', this._reset, this); - - if (this.options.zoomAnimation) { - map.on({ - 'zoomanim': this._animateZoom, - 'zoomend': this._endZoomAnim, - 'moveend': this._reset - }, this); - } - - if(this.options.tileLoader) { - this._initTileLoader(); - } - - this._reset(); - }, - - _animateZoom: function (e) { - if (!this._animating) { - this._animating = true; - } - var back = this._backCanvas; - - back.width = this._canvas.width; - back.height = this._canvas.height; - - // paint current canvas in back canvas with trasnformation - var pos = this._canvas._leaflet_pos || { x: 0, y: 0 }; - back.getContext('2d').drawImage(this._canvas, 0, 0); - - L.DomUtil.setPosition(back, L.DomUtil.getPosition(this._canvas)); - - // hide original - this._canvas.style.display = 'none'; - back.style.display = 'block'; - var map = this._map; - var scale = map.getZoomScale(e.zoom); - var newCenter = map._latLngToNewLayerPoint(map.getCenter(), e.zoom, e.center); - var oldCenter = map._latLngToNewLayerPoint(e.center, e.zoom, e.center); - - var origin = { - x: newCenter.x - oldCenter.x + pos.x, - y: newCenter.y - oldCenter.y + pos.y, - }; - - var bg = back; - var transform = L.DomUtil.TRANSFORM; - setTimeout(function() { - bg.style[transform] = L.DomUtil.getTranslateString(origin) + ' scale(' + e.scale + ') '; - }, 0) - }, - - _endZoomAnim: function () { - this._animating = false; - this._canvas.style.display = 'block'; - this._backCanvas.style.display = 'none'; - this._backCanvas.style[L.DomUtil.TRANSFORM] = ''; - }, - - getCanvas: function() { - return this._canvas; - }, - - getAttribution: function() { - return this.options.attribution; - }, - - draw: function() { - return this._reset(); - }, - - onRemove: function (map) { - this._container.parentNode.removeChild(this._container); - map.off({ - 'viewreset': this._reset, - 'move': this._render, - 'moveend': this._reset, - 'resize': this._reset, - 'zoomanim': this._animateZoom, - 'zoomend': this._endZoomAnim - }, this); - }, - - addTo: function (map) { - map.addLayer(this); - return this; - }, - - setOpacity: function (opacity) { - this.options.opacity = opacity; - this._updateOpacity(); - return this; - }, - - setZIndex: function(zIndex) { - this._canvas.style.zIndex = zIndex; - if (this.options.zoomAnimation) { - this._backCanvas.style.zIndex = zIndex; - } - }, - - bringToFront: function () { - return this; - }, - - bringToBack: function () { - return this; - }, - - _reset: function () { - var size = this._map.getSize(); - this._canvas.width = size.x; - this._canvas.height = size.y; - - // fix position - var pos = L.DomUtil.getPosition(this._map.getPanes().mapPane); - if (pos) { - L.DomUtil.setPosition(this._canvas, { x: -pos.x, y: -pos.y }); - } - this.onResize(); - this._render(); - }, - - /* - _project: function(x) { - var point = this._map.latLngToLayerPoint(new L.LatLng(x[1], x[0])); - return [point.x, point.y]; - }, - */ - - _updateOpacity: function () { }, - - _render: function() { - if (this.currentAnimationFrame >= 0) { - this.cancelAnimationFrame.call(window, this.currentAnimationFrame); - } - this.currentAnimationFrame = this.requestAnimationFrame.call(window, this.render); - }, - - // use direct: true if you are inside an animation frame call - redraw: function(direct) { - if (direct) { - this.render(); - } else { - this._render(); - } - }, - - onResize: function() { - }, - - render: function() { - throw new Error('render function should be implemented'); - } - -}); - -} //L defined -if(typeof(L) !== 'undefined') { -/** - * torque layer - */ -L.TorqueLayer = L.CanvasLayer.extend({ - - providers: { - 'sql_api': torque.providers.json, - 'url_template': torque.providers.jsonarray, - 'windshaft': torque.providers.windshaft - }, - - renderers: { - 'point': torque.renderer.Point, - 'pixel': torque.renderer.Rectangle - }, - - initialize: function(options) { - var self = this; - if (!torque.isBrowserSupported()) { - throw new Error("browser is not supported by torque"); - } - options.tileLoader = true; - this.key = 0; - this.prevRenderedKey = 0; - if (options.cartocss) { - _.extend(options, torque.common.TorqueLayer.optionsFromCartoCSS(options.cartocss)); - } - - options.resolution = options.resolution || 2; - options.steps = options.steps || 100; - options.visible = options.visible === undefined ? true: options.visible; - this.hidden = !options.visible; - - this.animator = new torque.Animator(function(time) { - var k = time | 0; - if(self.key !== k) { - self.setKey(k, { direct: true }); - } - }, torque.clone(options)); - - this.play = this.animator.start.bind(this.animator); - this.stop = this.animator.stop.bind(this.animator); - this.pause = this.animator.pause.bind(this.animator); - this.toggle = this.animator.toggle.bind(this.animator); - this.setDuration = this.animator.duration.bind(this.animator); - this.isRunning = this.animator.isRunning.bind(this.animator); - - - L.CanvasLayer.prototype.initialize.call(this, options); - - this.options.renderer = this.options.renderer || 'point'; - this.options.provider = this.options.provider || 'windshaft'; - - options.ready = function() { - self.fire("change:bounds", { - bounds: self.provider.getBounds() - }); - self.animator.steps(self.provider.getSteps()); - self.animator.rescale(); - self.fire('change:steps', { - steps: self.provider.getSteps() - }); - self.setKey(self.key); - }; - - this.provider = new this.providers[this.options.provider](options); - this.renderer = new this.renderers[this.options.renderer](this.getCanvas(), options); - - - // for each tile shown on the map request the data - this.on('tileAdded', function(t) { - var tileData = this.provider.getTileData(t, t.zoom, function(tileData) { - // don't load tiles that are not being shown - if (t.zoom !== self._map.getZoom()) return; - self._tileLoaded(t, tileData); - self._clearTileCaches(); - if (tileData) { - self.redraw(); - } - }); - }, this); - - - }, - - _clearTileCaches: function() { - var t, tile; - for(t in this._tiles) { - tile = this._tiles[t]; - if (tile && tile._tileCache) { - tile._tileCache = null; - } - } - }, - - _clearCaches: function() { - this.renderer && this.renderer.clearSpriteCache(); - this._clearTileCaches(); - }, - - onAdd: function (map) { - map.on({ - 'zoomend': this._clearCaches, - 'zoomstart': this._pauseOnZoom, - }, this); - - map.on({ - 'zoomend': this._resumeOnZoom - }, this); - L.CanvasLayer.prototype.onAdd.call(this, map); - }, - - onRemove: function(map) { - this._removeTileLoader(); - map.off({ - 'zoomend': this._clearCaches, - 'zoomstart': this._pauseOnZoom, - }, this); - map.off({ - 'zoomend': this._resumeOnZoom - }, this); - L.CanvasLayer.prototype.onRemove.call(this, map); - }, - - _pauseOnZoom: function() { - this.wasRunning = this.isRunning(); - if (this.wasRunning) { - this.pause(); - } - }, - - _resumeOnZoom: function() { - if (this.wasRunning) { - this.play(); - } - }, - - hide: function() { - if(this.hidden) return this; - this.pause(); - this.clear(); - this.hidden = true; - return this; - }, - - show: function() { - if(!this.hidden) return this; - this.hidden = false; - this.play(); - return this; - }, - - setSQL: function(sql) { - if (!this.provider || !this.provider.setSQL) { - throw new Error("this provider does not support SQL"); - } - this.provider.setSQL(sql); - this._reloadTiles(); - return this; - }, - - setBlendMode: function(_) { - this.renderer.setBlendMode(_); - this.redraw(); - }, - - setSteps: function(steps) { - this.provider.setSteps(steps); - this._reloadTiles(); - }, - - setColumn: function(column, isTime) { - this.provider.setColumn(column, isTime); - this._reloadTiles(); - }, - - getTimeBounds: function() { - return this.provider && this.provider.getKeySpan(); - }, - - clear: function() { - var canvas = this.getCanvas(); - canvas.width = canvas.width; - }, - - /** - * render the selectef key - * don't call this function directly, it's called by - * requestAnimationFrame. Use redraw to refresh it - */ - render: function() { - if(this.hidden) return; - var t, tile, pos; - var canvas = this.getCanvas(); - canvas.width = canvas.width; - var ctx = canvas.getContext('2d'); - - for(t in this._tiles) { - tile = this._tiles[t]; - if (tile) { - // clear cache - if (this.animator.isRunning()) { - tile._tileCache = null; - } - - pos = this.getTilePos(tile.coord); - ctx.setTransform(1, 0, 0, 1, pos.x, pos.y); - - if (tile._tileCache) { - // when the tile has a cached image just render it and avoid to render - // all the points - this.renderer._ctx.drawImage(tile._tileCache, 0, 0); - } else { - this.renderer.renderTile(tile, this.key); - } - } - } - - // prepare caches if the animation is not running - // don't cache if the key has just changed, this avoids to cache - // when the user is dragging, it only cache when the map is still - if (!this.animator.isRunning() && this.key === this.prevRenderedKey) { - var tile_size = this.renderer.TILE_SIZE; - for(t in this._tiles) { - tile = this._tiles[t]; - if (tile && !tile._tileCache) { - var c = tile._tileCache = document.createElement('canvas'); - c.width = c.height = tile_size; - pos = this.getTilePos(tile.coord); - c.getContext('2d').drawImage(this.getCanvas(), pos.x, pos.y, tile_size, tile_size, 0, 0, tile_size, tile_size); - } - } - } - - this.prevRenderedKey = this.key; - - }, - - /** - * set key to be shown. If it's a single value - * it renders directly, if it's an array it renders - * accumulated - */ - setKey: function(key, options) { - this.key = key; - this.animator.step(key); - this._clearTileCaches(); - this.redraw(options && options.direct); - this.fire('change:time', { time: this.getTime(), step: this.key }); - }, - - /** - * helper function, does the same than ``setKey`` but only - * accepts scalars. - */ - setStep: function(time) { - if(time === undefined || time.length !== undefined) { - throw new Error("setTime only accept scalars"); - } - this.setKey(time); - }, - - /** - * transform from animation step to Date object - * that contains the animation time - * - * ``step`` should be between 0 and ``steps - 1`` - */ - stepToTime: function(step) { - var times = this.provider.getKeySpan(); - var time = times.start + (times.end - times.start)*(step/this.provider.getSteps()); - return new Date(time); - }, - - getStep: function() { - return this.key; - }, - - /** - * returns the animation time defined by the data - * in the defined column. Date object - */ - getTime: function() { - return this.stepToTime(this.key); - }, - - /** - * returns an object with the start and end times - */ - getTimeSpan: function() { - var times = this.provider.getKeySpan(); - }, - - /** - * set the cartocss for the current renderer - */ - setCartoCSS: function(cartocss) { - if (!this.renderer) throw new Error('renderer is not valid'); - var shader = new carto.RendererJS().render(cartocss); - this.renderer.setShader(shader); - - // provider options - var options = torque.common.TorqueLayer.optionsFromLayer(shader.findLayer({ name: 'Map' })); - this.provider.setCartoCSS && this.provider.setCartoCSS(cartocss); - if(this.provider.setOptions(options)) { - this._reloadTiles(); - } - - _.extend(this.options, options); - - // animator options - if (options.animationDuration) { - this.animator.duration(options.animationDuration); - } - - this.redraw(); - return this; - }, - - /** - * get active points for a step in active zoom - * returns a list of bounding boxes [[] , [], []] - * empty list if there is no active pixels - */ - getActivePointsBBox: function(step) { - var positions = []; - for(var t in this._tiles) { - var tile = this._tiles[t]; - positions = positions.concat(this.renderer.getActivePointsBBox(tile, step)); - } - return positions; - }, - - /** - * return the value for position relative to map coordinates. null for no value - */ - getValueForPos: function(x, y, step) { - step = step === undefined ? this.key: step; - var t, tile, pos, value = null, xx, yy; - for(t in this._tiles) { - tile = this._tiles[t]; - pos = this.getTilePos(tile.coord); - xx = x - pos.x; - yy = y - pos.y; - if (xx >= 0 && yy >= 0 && xx < this.renderer.TILE_SIZE && yy <= this.renderer.TILE_SIZE) { - value = this.renderer.getValueFor(tile, step, xx, yy); - } - if (value !== null) { - return value; - } - } - return null; - } - -}); - -} //L defined - -(function() { - -if(typeof(google) == "undefined" || typeof(google.maps) == "undefined") - return; - -var GMapsTorqueLayerView = function(layerModel, gmapsMap) { - - var extra = layerModel.get('extra_params'); - layerModel.attributes.attribution = cdb.config.get('cartodb_attributions'); - cdb.geo.GMapsLayerView.call(this, layerModel, this, gmapsMap); - - var query = this._getQuery(layerModel); - torque.GMapsTorqueLayer.call(this, { - table: layerModel.get('table_name'), - user: layerModel.get('user_name'), - column: layerModel.get('property'), - blendmode: layerModel.get('torque-blend-mode'), - resolution: 1, - //TODO: manage time columns - countby: 'count(cartodb_id)', - sql_api_domain: layerModel.get('sql_api_domain'), - sql_api_protocol: layerModel.get('sql_api_protocol'), - sql_api_port: layerModel.get('sql_api_port'), - tiler_protocol: layerModel.get('tiler_protocol'), - tiler_domain: layerModel.get('tiler_domain'), - tiler_port: layerModel.get('tiler_port'), - stat_tag: layerModel.get('stat_tag'), - animationDuration: layerModel.get('torque-duration'), - steps: layerModel.get('torque-steps'), - sql: query, - visible: layerModel.get('visible'), - extra_params: { - api_key: extra ? extra.map_key: '' - }, - map: gmapsMap, - cartodb_logo: layerModel.get('cartodb_logo'), - attribution: layerModel.get('attribution'), - cdn_url: layerModel.get('no_cdn') ? null: (layerModel.get('cdn_url') || cdb.CDB_HOST), - cartocss: layerModel.get('cartocss') || layerModel.get('tile_style'), - named_map: layerModel.get('named_map'), - auth_token: layerModel.get('auth_token'), - no_cdn: layerModel.get('no_cdn') - }); - - //this.setCartoCSS(this.model.get('tile_style')); - if (layerModel.get('visible')) { - this.play(); - } - -}; - -_.extend( - GMapsTorqueLayerView.prototype, - cdb.geo.GMapsLayerView.prototype, - torque.GMapsTorqueLayer.prototype, - { - - _update: function() { - var changed = this.model.changedAttributes(); - if(changed === false) return; - changed.tile_style && this.setCartoCSS(this.model.get('tile_style')); - if ('query' in changed || 'query_wrapper' in changed) { - this.setSQL(this._getQuery(this.model)); - } - if ('visible' in changed) - this.model.get('visible') ? this.show(): this.hide(); - }, - - _getQuery: function(layerModel) { - var query = layerModel.get('query'); - var qw = layerModel.get('query_wrapper'); - if(qw) { - query = _.template(qw)({ sql: query || ('select * from ' + layerModel.get('table_name')) }); - } - return query; - }, - - refreshView: function() { - //TODO: update screen - }, - - onAdd: function() { - torque.GMapsTorqueLayer.prototype.onAdd.apply(this); - // Add CartoDB logo - if (this.options.cartodb_logo != false) - cdb.geo.common.CartoDBLogo.addWadus({ left: 74, bottom:8 }, 2000, this.map.getDiv()) - }, - - onTilesLoaded: function() { - //this.trigger('load'); - Backbone.Events.trigger.call(this, 'load'); - }, - - onTilesLoading: function() { - Backbone.Events.trigger.call(this, 'loading'); - } - -}); - - -cdb.geo.GMapsTorqueLayerView = GMapsTorqueLayerView; - - -})(); - -(function() { - -if(typeof(L) === "undefined") - return; - -/** - * leaflet torque layer - */ -var LeafLetTorqueLayer = L.TorqueLayer.extend({ - - initialize: function(layerModel, leafletMap) { - var extra = layerModel.get('extra_params'); - layerModel.attributes.attribution = cdb.config.get('cartodb_attributions'); - - var query = this._getQuery(layerModel); - - // initialize the base layers - L.TorqueLayer.prototype.initialize.call(this, { - table: layerModel.get('table_name'), - user: layerModel.get('user_name'), - column: layerModel.get('property'), - blendmode: layerModel.get('torque-blend-mode'), - resolution: 1, - //TODO: manage time columns - countby: 'count(cartodb_id)', - sql_api_domain: layerModel.get('sql_api_domain'), - sql_api_protocol: layerModel.get('sql_api_protocol'), - sql_api_port: layerModel.get('sql_api_port'), - tiler_protocol: layerModel.get('tiler_protocol'), - tiler_domain: layerModel.get('tiler_domain'), - tiler_port: layerModel.get('tiler_port'), - stat_tag: layerModel.get('stat_tag'), - animationDuration: layerModel.get('torque-duration'), - steps: layerModel.get('torque-steps'), - sql: query, - visible: layerModel.get('visible'), - extra_params: { - api_key: extra ? extra.map_key: '' - }, - cartodb_logo: layerModel.get('cartodb_logo'), - attribution: layerModel.get('attribution'), - cdn_url: layerModel.get('no_cdn') ? null: (layerModel.get('cdn_url') || cdb.CDB_HOST), - cartocss: layerModel.get('cartocss') || layerModel.get('tile_style'), - named_map: layerModel.get('named_map'), - auth_token: layerModel.get('auth_token'), - no_cdn: layerModel.get('no_cdn'), - dynamic_cdn: layerModel.get('dynamic_cdn'), - instanciateCallback: function() { - var cartocss = layerModel.get('cartocss') || layerModel.get('tile_style'); - - return '_cdbct_' + cartodb.uniqueCallbackName(cartocss + query) - } - }); - - cdb.geo.LeafLetLayerView.call(this, layerModel, this, leafletMap); - - // match leaflet events with backbone events - this.fire = this.trigger; - - //this.setCartoCSS(layerModel.get('tile_style')); - if (layerModel.get('visible')) { - this.play(); - } - - this.bind('tilesLoaded', function() { - this.trigger('load'); - }, this); - - this.bind('tilesLoading', function() { - this.trigger('loading'); - }, this); - - }, - - onAdd: function(map) { - L.TorqueLayer.prototype.onAdd.apply(this, [map]); - // Add CartoDB logo - if (this.options.cartodb_logo != false) - cdb.geo.common.CartoDBLogo.addWadus({ left:8, bottom:8 }, 0, map._container) - }, - - _getQuery: function(layerModel) { - var query = layerModel.get('query'); - var qw = layerModel.get('query_wrapper'); - if(qw) { - query = _.template(qw)({ sql: query || ('select * from ' + layerModel.get('table_name')) }); - } - return query; - }, - - _modelUpdated: function(model) { - var changed = this.model.changedAttributes(); - if(changed === false) return; - changed.tile_style && this.setCartoCSS(this.model.get('tile_style')); - if ('query' in changed || 'query_wrapper' in changed) { - this.setSQL(this._getQuery(this.model)); - } - - if ('visible' in changed) - this.model.get('visible') ? this.show(): this.hide(); - - } - - - -}); - -_.extend(LeafLetTorqueLayer.prototype, cdb.geo.LeafLetLayerView.prototype); - -cdb.geo.LeafLetTorqueLayer = LeafLetTorqueLayer; - -})(); -cdb.geo.ui.TimeSlider = cdb.geo.ui.InfoBox.extend({ - - DEFAULT_OFFSET_TOP: 30, - className: 'cartodb-timeslider', - - defaultTemplate: - "
    " + - "
  • pause
  • " + - "
  • " + - "
  • " + - "
" - , - - events: { - "click .button": "toggleTime", - "click .time": "_onClickTime", - "dragstart": "_stopPropagation", - "mousedown": "_stopPropagation", - "touchstart": "_stopPropagation", - "MSPointerDown": "_stopPropagation", - "dblclick": "_stopPropagation", - "mousewheel": "_stopPropagation", - "DOMMouseScroll": "_stopPropagation", - "click": "_stopPropagation" - }, - - initialize: function() { - _.bindAll(this, '_stop', '_start', '_slide', '_bindLayer', '_unbindLayer', 'updateSliderRange', 'updateSlider', 'updateTime'); - var self = this; - this.options.template = this.options.template || this.defaultTemplate; - this.options.position = 'bottom|left'; - this.options.width = null; - - // Control variable to know if the layer was - // running before touching the slider - this.wasRunning = false; - - this._bindLayer(this.options.layer); - this.on('clean', this._unbindLayer); - cdb.geo.ui.InfoBox.prototype.initialize.call(this); - - }, - - setLayer: function(layer) { - this._unbindLayer(); - this._bindLayer(layer); - this._initSlider(); - }, - - _bindLayer: function(layer) { - this.torqueLayer = layer; - this.torqueLayer.on('change:time', this.updateSlider); - this.torqueLayer.on('change:time', this.updateTime); - this.torqueLayer.on('change:steps', this.updateSliderRange); - return this; - }, - - _unbindLayer: function() { - this.torqueLayer.off('change:time', this.updateSlider); - this.torqueLayer.off('change:time', this.updateTime); - this.torqueLayer.off('change:steps', this.updateSliderRange); - return this; - }, - - updateSlider: function(changes) { - this.$(".slider" ).slider({ value: changes.step }); - }, - - updateSliderRange: function(changes) { - this.$(".slider" ).slider({ max: changes.steps }); - }, - - // each time time changes, move the slider - updateTime: function(changes) { - var self = this; - var tb = self.torqueLayer.getTimeBounds(); - if (!tb) return; - if (tb.columnType === 'date' || this.options.force_format_date) { - if (tb && tb.start !== undefined) { - var f = self.options.formatter || self.formaterForRange(tb.start, tb.end); - // avoid showing invalid dates - if (!_.isNaN(changes.time.getYear())) { - self.$('.value').text(f(changes.time)); - } - } - } else { - self.$('.value').text(changes.step); - } - }, - - formatter: function(_) { - this.options.formatter = _; - }, - - formaterForRange: function(start, end) { - start = start.getTime ? start.getTime(): start; - end = end.getTime ? end.getTime(): end; - var span = (end - start)/1000; - var ONE_DAY = 3600*24; - var ONE_YEAR = ONE_DAY * 31 * 12; - function pad(n) { return n < 10 ? '0' + n : n; }; - // lest than a day - if (span < ONE_DAY) return function(t) { return pad(t.getUTCHours()) + ":" + pad(t.getUTCMinutes()); }; - if (span < ONE_YEAR) return function(t) { return pad(t.getUTCMonth() + 1) + "/" + pad(t.getUTCDate()) + "/" + pad(t.getUTCFullYear()); }; - return function(t) { return pad(t.getUTCMonth() + 1) + "/" + pad(t.getUTCFullYear()); }; - }, - - _slide: function(e, ui) { - this.killEvent(e); - var step = ui.value; - this.torqueLayer.setStep(step); - }, - - _start: function(e, ui) { - if(this.torqueLayer.isRunning()) { - this.wasRunning = true; - this.toggleTime(); - } - }, - - _stop: function(e, ui) { - if (this.wasRunning) { - this.toggleTime() - } - - this.wasRunning = false; - }, - - _initSlider: function() { - var torqueLayer = this.torqueLayer; - var slider = this.$(".slider"); - slider.slider({ - range: 'min', - min: 0, - max: this.torqueLayer.options.steps, - value: 0, - step: 1, // - value: this.torqueLayer.getStep(), - stop: this._stop, - start: this._start, - slide: this._slide - }); - }, - - toggleTime: function(e) { - this.killEvent(e); - this.torqueLayer.toggle(); - this.$('.button') - [(this.torqueLayer.isRunning() ? 'addClass': 'removeClass')]('stop') - .attr('href','#/' + (this.torqueLayer.isRunning() ? 'pause': 'play')) - .html(this.torqueLayer.isRunning() ? 'pause': 'play'); - }, - - enable: function() {}, - - _stopPropagation: function(ev) { - ev.stopPropagation(); - }, - - _onClickTime: function() { - this.trigger("time_clicked", this); - }, - - render: function() { - cdb.geo.ui.InfoBox.prototype.render.apply(this, arguments); - this._initSlider(); - return this; - } - -}); - -/*! - * jQuery UI 1.8.23 - * - * Copyright 2012, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://docs.jquery.com/UI - */ -(function( $, undefined ) { - -// prevent duplicate loading -// this is only a problem because we proxy existing functions -// and we don't want to double proxy them -$.ui = $.ui || {}; -if ( $.ui.version ) { - return; -} - -$.extend( $.ui, { - version: "1.8.23", - - keyCode: { - ALT: 18, - BACKSPACE: 8, - CAPS_LOCK: 20, - COMMA: 188, - COMMAND: 91, - COMMAND_LEFT: 91, // COMMAND - COMMAND_RIGHT: 93, - CONTROL: 17, - DELETE: 46, - DOWN: 40, - END: 35, - ENTER: 13, - ESCAPE: 27, - HOME: 36, - INSERT: 45, - LEFT: 37, - MENU: 93, // COMMAND_RIGHT - NUMPAD_ADD: 107, - NUMPAD_DECIMAL: 110, - NUMPAD_DIVIDE: 111, - NUMPAD_ENTER: 108, - NUMPAD_MULTIPLY: 106, - NUMPAD_SUBTRACT: 109, - PAGE_DOWN: 34, - PAGE_UP: 33, - PERIOD: 190, - RIGHT: 39, - SHIFT: 16, - SPACE: 32, - TAB: 9, - UP: 38, - WINDOWS: 91 // COMMAND - } -}); - -// plugins -$.fn.extend({ - propAttr: $.fn.prop || $.fn.attr, - - _focus: $.fn.focus, - focus: function( delay, fn ) { - return typeof delay === "number" ? - this.each(function() { - var elem = this; - setTimeout(function() { - $( elem ).focus(); - if ( fn ) { - fn.call( elem ); - } - }, delay ); - }) : - this._focus.apply( this, arguments ); - }, - - scrollParent: function() { - var scrollParent; - if (($.browser.msie && (/(static|relative)/).test(this.css('position'))) || (/absolute/).test(this.css('position'))) { - scrollParent = this.parents().filter(function() { - return (/(relative|absolute|fixed)/).test($.curCSS(this,'position',1)) && (/(auto|scroll)/).test($.curCSS(this,'overflow',1)+$.curCSS(this,'overflow-y',1)+$.curCSS(this,'overflow-x',1)); - }).eq(0); - } else { - scrollParent = this.parents().filter(function() { - return (/(auto|scroll)/).test($.curCSS(this,'overflow',1)+$.curCSS(this,'overflow-y',1)+$.curCSS(this,'overflow-x',1)); - }).eq(0); - } - - return (/fixed/).test(this.css('position')) || !scrollParent.length ? $(document) : scrollParent; - }, - - zIndex: function( zIndex ) { - if ( zIndex !== undefined ) { - return this.css( "zIndex", zIndex ); - } - - if ( this.length ) { - var elem = $( this[ 0 ] ), position, value; - while ( elem.length && elem[ 0 ] !== document ) { - // Ignore z-index if position is set to a value where z-index is ignored by the browser - // This makes behavior of this function consistent across browsers - // WebKit always returns auto if the element is positioned - position = elem.css( "position" ); - if ( position === "absolute" || position === "relative" || position === "fixed" ) { - // IE returns 0 when zIndex is not specified - // other browsers return a string - // we ignore the case of nested elements with an explicit value of 0 - //
- value = parseInt( elem.css( "zIndex" ), 10 ); - if ( !isNaN( value ) && value !== 0 ) { - return value; - } - } - elem = elem.parent(); - } - } - - return 0; - }, - - disableSelection: function() { - return this.bind( ( $.support.selectstart ? "selectstart" : "mousedown" ) + - ".ui-disableSelection", function( event ) { - event.preventDefault(); - }); - }, - - enableSelection: function() { - return this.unbind( ".ui-disableSelection" ); - } -}); - -// support: jQuery <1.8 -if ( !$( "" ).outerWidth( 1 ).jquery ) { - $.each( [ "Width", "Height" ], function( i, name ) { - var side = name === "Width" ? [ "Left", "Right" ] : [ "Top", "Bottom" ], - type = name.toLowerCase(), - orig = { - innerWidth: $.fn.innerWidth, - innerHeight: $.fn.innerHeight, - outerWidth: $.fn.outerWidth, - outerHeight: $.fn.outerHeight - }; - - function reduce( elem, size, border, margin ) { - $.each( side, function() { - size -= parseFloat( $.curCSS( elem, "padding" + this, true) ) || 0; - if ( border ) { - size -= parseFloat( $.curCSS( elem, "border" + this + "Width", true) ) || 0; - } - if ( margin ) { - size -= parseFloat( $.curCSS( elem, "margin" + this, true) ) || 0; - } - }); - return size; - } - - $.fn[ "inner" + name ] = function( size ) { - if ( size === undefined ) { - return orig[ "inner" + name ].call( this ); - } - - return this.each(function() { - $( this ).css( type, reduce( this, size ) + "px" ); - }); - }; - - $.fn[ "outer" + name] = function( size, margin ) { - if ( typeof size !== "number" ) { - return orig[ "outer" + name ].call( this, size ); - } - - return this.each(function() { - $( this).css( type, reduce( this, size, true, margin ) + "px" ); - }); - }; - }); -} - -// selectors -function focusable( element, isTabIndexNotNaN ) { - var nodeName = element.nodeName.toLowerCase(); - if ( "area" === nodeName ) { - var map = element.parentNode, - mapName = map.name, - img; - if ( !element.href || !mapName || map.nodeName.toLowerCase() !== "map" ) { - return false; - } - img = $( "img[usemap=#" + mapName + "]" )[0]; - return !!img && visible( img ); - } - return ( /input|select|textarea|button|object/.test( nodeName ) - ? !element.disabled - : "a" == nodeName - ? element.href || isTabIndexNotNaN - : isTabIndexNotNaN) - // the element and all of its ancestors must be visible - && visible( element ); -} - -function visible( element ) { - return !$( element ).parents().andSelf().filter(function() { - return $.curCSS( this, "visibility" ) === "hidden" || - $.expr.filters.hidden( this ); - }).length; -} - -$.extend( $.expr[ ":" ], { - data: $.expr.createPseudo ? - $.expr.createPseudo(function( dataName ) { - return function( elem ) { - return !!$.data( elem, dataName ); - }; - }) : - // support: jQuery <1.8 - function( elem, i, match ) { - return !!$.data( elem, match[ 3 ] ); - }, - - focusable: function( element ) { - return focusable( element, !isNaN( $.attr( element, "tabindex" ) ) ); - }, - - tabbable: function( element ) { - var tabIndex = $.attr( element, "tabindex" ), - isTabIndexNaN = isNaN( tabIndex ); - return ( isTabIndexNaN || tabIndex >= 0 ) && focusable( element, !isTabIndexNaN ); - } -}); - -// support -$(function() { - var body = document.body, - div = body.appendChild( div = document.createElement( "div" ) ); - - // access offsetHeight before setting the style to prevent a layout bug - // in IE 9 which causes the elemnt to continue to take up space even - // after it is removed from the DOM (#8026) - div.offsetHeight; - - $.extend( div.style, { - minHeight: "100px", - height: "auto", - padding: 0, - borderWidth: 0 - }); - - $.support.minHeight = div.offsetHeight === 100; - $.support.selectstart = "onselectstart" in div; - - // set display to none to avoid a layout bug in IE - // http://dev.jquery.com/ticket/4014 - body.removeChild( div ).style.display = "none"; -}); - -// jQuery <1.4.3 uses curCSS, in 1.4.3 - 1.7.2 curCSS = css, 1.8+ only has css -if ( !$.curCSS ) { - $.curCSS = $.css; -} - - - - - -// deprecated -$.extend( $.ui, { - // $.ui.plugin is deprecated. Use the proxy pattern instead. - plugin: { - add: function( module, option, set ) { - var proto = $.ui[ module ].prototype; - for ( var i in set ) { - proto.plugins[ i ] = proto.plugins[ i ] || []; - proto.plugins[ i ].push( [ option, set[ i ] ] ); - } - }, - call: function( instance, name, args ) { - var set = instance.plugins[ name ]; - if ( !set || !instance.element[ 0 ].parentNode ) { - return; - } - - for ( var i = 0; i < set.length; i++ ) { - if ( instance.options[ set[ i ][ 0 ] ] ) { - set[ i ][ 1 ].apply( instance.element, args ); - } - } - } - }, - - // will be deprecated when we switch to jQuery 1.4 - use jQuery.contains() - contains: function( a, b ) { - return document.compareDocumentPosition ? - a.compareDocumentPosition( b ) & 16 : - a !== b && a.contains( b ); - }, - - // only used by resizable - hasScroll: function( el, a ) { - - //If overflow is hidden, the element might have extra content, but the user wants to hide it - if ( $( el ).css( "overflow" ) === "hidden") { - return false; - } - - var scroll = ( a && a === "left" ) ? "scrollLeft" : "scrollTop", - has = false; - - if ( el[ scroll ] > 0 ) { - return true; - } - - // TODO: determine which cases actually cause this to happen - // if the element doesn't have the scroll set, see if it's possible to - // set the scroll - el[ scroll ] = 1; - has = ( el[ scroll ] > 0 ); - el[ scroll ] = 0; - return has; - }, - - // these are odd functions, fix the API or move into individual plugins - isOverAxis: function( x, reference, size ) { - //Determines when x coordinate is over "b" element axis - return ( x > reference ) && ( x < ( reference + size ) ); - }, - isOver: function( y, x, top, left, height, width ) { - //Determines when x, y coordinates is over "b" element - return $.ui.isOverAxis( y, top, height ) && $.ui.isOverAxis( x, left, width ); - } -}); - -})( jQuery ); -/*! - * jQuery UI Widget 1.8.23 - * - * Copyright 2012, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://docs.jquery.com/UI/Widget - */ -(function( $, undefined ) { - -// jQuery 1.4+ -if ( $.cleanData ) { - var _cleanData = $.cleanData; - $.cleanData = function( elems ) { - for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) { - try { - $( elem ).triggerHandler( "remove" ); - // http://bugs.jquery.com/ticket/8235 - } catch( e ) {} - } - _cleanData( elems ); - }; -} else { - var _remove = $.fn.remove; - $.fn.remove = function( selector, keepData ) { - return this.each(function() { - if ( !keepData ) { - if ( !selector || $.filter( selector, [ this ] ).length ) { - $( "*", this ).add( [ this ] ).each(function() { - try { - $( this ).triggerHandler( "remove" ); - // http://bugs.jquery.com/ticket/8235 - } catch( e ) {} - }); - } - } - return _remove.call( $(this), selector, keepData ); - }); - }; -} - -$.widget = function( name, base, prototype ) { - var namespace = name.split( "." )[ 0 ], - fullName; - name = name.split( "." )[ 1 ]; - fullName = namespace + "-" + name; - - if ( !prototype ) { - prototype = base; - base = $.Widget; - } - - // create selector for plugin - $.expr[ ":" ][ fullName ] = function( elem ) { - return !!$.data( elem, name ); - }; - - $[ namespace ] = $[ namespace ] || {}; - $[ namespace ][ name ] = function( options, element ) { - // allow instantiation without initializing for simple inheritance - if ( arguments.length ) { - this._createWidget( options, element ); - } - }; - - var basePrototype = new base(); - // we need to make the options hash a property directly on the new instance - // otherwise we'll modify the options hash on the prototype that we're - // inheriting from -// $.each( basePrototype, function( key, val ) { -// if ( $.isPlainObject(val) ) { -// basePrototype[ key ] = $.extend( {}, val ); -// } -// }); - basePrototype.options = $.extend( true, {}, basePrototype.options ); - $[ namespace ][ name ].prototype = $.extend( true, basePrototype, { - namespace: namespace, - widgetName: name, - widgetEventPrefix: $[ namespace ][ name ].prototype.widgetEventPrefix || name, - widgetBaseClass: fullName - }, prototype ); - - $.widget.bridge( name, $[ namespace ][ name ] ); -}; - -$.widget.bridge = function( name, object ) { - $.fn[ name ] = function( options ) { - var isMethodCall = typeof options === "string", - args = Array.prototype.slice.call( arguments, 1 ), - returnValue = this; - - // allow multiple hashes to be passed on init - options = !isMethodCall && args.length ? - $.extend.apply( null, [ true, options ].concat(args) ) : - options; - - // prevent calls to internal methods - if ( isMethodCall && options.charAt( 0 ) === "_" ) { - return returnValue; - } - - if ( isMethodCall ) { - this.each(function() { - var instance = $.data( this, name ), - methodValue = instance && $.isFunction( instance[options] ) ? - instance[ options ].apply( instance, args ) : - instance; - // TODO: add this back in 1.9 and use $.error() (see #5972) -// if ( !instance ) { -// throw "cannot call methods on " + name + " prior to initialization; " + -// "attempted to call method '" + options + "'"; -// } -// if ( !$.isFunction( instance[options] ) ) { -// throw "no such method '" + options + "' for " + name + " widget instance"; -// } -// var methodValue = instance[ options ].apply( instance, args ); - if ( methodValue !== instance && methodValue !== undefined ) { - returnValue = methodValue; - return false; - } - }); - } else { - this.each(function() { - var instance = $.data( this, name ); - if ( instance ) { - instance.option( options || {} )._init(); - } else { - $.data( this, name, new object( options, this ) ); - } - }); - } - - return returnValue; - }; -}; - -$.Widget = function( options, element ) { - // allow instantiation without initializing for simple inheritance - if ( arguments.length ) { - this._createWidget( options, element ); - } -}; - -$.Widget.prototype = { - widgetName: "widget", - widgetEventPrefix: "", - options: { - disabled: false - }, - _createWidget: function( options, element ) { - // $.widget.bridge stores the plugin instance, but we do it anyway - // so that it's stored even before the _create function runs - $.data( element, this.widgetName, this ); - this.element = $( element ); - this.options = $.extend( true, {}, - this.options, - this._getCreateOptions(), - options ); - - var self = this; - this.element.bind( "remove." + this.widgetName, function() { - self.destroy(); - }); - - this._create(); - this._trigger( "create" ); - this._init(); - }, - _getCreateOptions: function() { - return $.metadata && $.metadata.get( this.element[0] )[ this.widgetName ]; - }, - _create: function() {}, - _init: function() {}, - - destroy: function() { - this.element - .unbind( "." + this.widgetName ) - .removeData( this.widgetName ); - this.widget() - .unbind( "." + this.widgetName ) - .removeAttr( "aria-disabled" ) - .removeClass( - this.widgetBaseClass + "-disabled " + - "ui-state-disabled" ); - }, - - widget: function() { - return this.element; - }, - - option: function( key, value ) { - var options = key; - - if ( arguments.length === 0 ) { - // don't return a reference to the internal hash - return $.extend( {}, this.options ); - } - - if (typeof key === "string" ) { - if ( value === undefined ) { - return this.options[ key ]; - } - options = {}; - options[ key ] = value; - } - - this._setOptions( options ); - - return this; - }, - _setOptions: function( options ) { - var self = this; - $.each( options, function( key, value ) { - self._setOption( key, value ); - }); - - return this; - }, - _setOption: function( key, value ) { - this.options[ key ] = value; - - if ( key === "disabled" ) { - this.widget() - [ value ? "addClass" : "removeClass"]( - this.widgetBaseClass + "-disabled" + " " + - "ui-state-disabled" ) - .attr( "aria-disabled", value ); - } - - return this; - }, - - enable: function() { - return this._setOption( "disabled", false ); - }, - disable: function() { - return this._setOption( "disabled", true ); - }, - - _trigger: function( type, event, data ) { - var prop, orig, - callback = this.options[ type ]; - - data = data || {}; - event = $.Event( event ); - event.type = ( type === this.widgetEventPrefix ? - type : - this.widgetEventPrefix + type ).toLowerCase(); - // the original event may come from any element - // so we need to reset the target on the new event - event.target = this.element[ 0 ]; - - // copy original event properties over to the new event - orig = event.originalEvent; - if ( orig ) { - for ( prop in orig ) { - if ( !( prop in event ) ) { - event[ prop ] = orig[ prop ]; - } - } - } - - this.element.trigger( event, data ); - - return !( $.isFunction(callback) && - callback.call( this.element[0], event, data ) === false || - event.isDefaultPrevented() ); - } -}; - -})( jQuery ); -/*! - * jQuery UI Mouse 1.8.23 - * - * Copyright 2012, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://docs.jquery.com/UI/Mouse - * - * Depends: - * jquery.ui.widget.js - */ -(function( $, undefined ) { - -var mouseHandled = false; -$( document ).mouseup( function( e ) { - mouseHandled = false; -}); - -$.widget("ui.mouse", { - options: { - cancel: ':input,option', - distance: 1, - delay: 0 - }, - _mouseInit: function() { - var self = this; - - this.element - .bind('mousedown.'+this.widgetName, function(event) { - return self._mouseDown(event); - }) - .bind('click.'+this.widgetName, function(event) { - if (true === $.data(event.target, self.widgetName + '.preventClickEvent')) { - $.removeData(event.target, self.widgetName + '.preventClickEvent'); - event.stopImmediatePropagation(); - return false; - } - }); - - this.started = false; - }, - - // TODO: make sure destroying one instance of mouse doesn't mess with - // other instances of mouse - _mouseDestroy: function() { - this.element.unbind('.'+this.widgetName); - if ( this._mouseMoveDelegate ) { - $(document) - .unbind('mousemove.'+this.widgetName, this._mouseMoveDelegate) - .unbind('mouseup.'+this.widgetName, this._mouseUpDelegate); - } - }, - - _mouseDown: function(event) { - // don't let more than one widget handle mouseStart - if( mouseHandled ) { return }; - - // we may have missed mouseup (out of window) - (this._mouseStarted && this._mouseUp(event)); - - this._mouseDownEvent = event; - - var self = this, - btnIsLeft = (event.which == 1), - // event.target.nodeName works around a bug in IE 8 with - // disabled inputs (#7620) - elIsCancel = (typeof this.options.cancel == "string" && event.target.nodeName ? $(event.target).closest(this.options.cancel).length : false); - if (!btnIsLeft || elIsCancel || !this._mouseCapture(event)) { - return true; - } - - this.mouseDelayMet = !this.options.delay; - if (!this.mouseDelayMet) { - this._mouseDelayTimer = setTimeout(function() { - self.mouseDelayMet = true; - }, this.options.delay); - } - - if (this._mouseDistanceMet(event) && this._mouseDelayMet(event)) { - this._mouseStarted = (this._mouseStart(event) !== false); - if (!this._mouseStarted) { - event.preventDefault(); - return true; - } - } - - // Click event may never have fired (Gecko & Opera) - if (true === $.data(event.target, this.widgetName + '.preventClickEvent')) { - $.removeData(event.target, this.widgetName + '.preventClickEvent'); - } - - // these delegates are required to keep context - this._mouseMoveDelegate = function(event) { - return self._mouseMove(event); - }; - this._mouseUpDelegate = function(event) { - return self._mouseUp(event); - }; - $(document) - .bind('mousemove.'+this.widgetName, this._mouseMoveDelegate) - .bind('mouseup.'+this.widgetName, this._mouseUpDelegate); - - event.preventDefault(); - - mouseHandled = true; - return true; - }, - - _mouseMove: function(event) { - // IE mouseup check - mouseup happened when mouse was out of window - if ($.browser.msie && !(document.documentMode >= 9) && !event.button) { - return this._mouseUp(event); - } - - if (this._mouseStarted) { - this._mouseDrag(event); - return event.preventDefault(); - } - - if (this._mouseDistanceMet(event) && this._mouseDelayMet(event)) { - this._mouseStarted = - (this._mouseStart(this._mouseDownEvent, event) !== false); - (this._mouseStarted ? this._mouseDrag(event) : this._mouseUp(event)); - } - - return !this._mouseStarted; - }, - - _mouseUp: function(event) { - $(document) - .unbind('mousemove.'+this.widgetName, this._mouseMoveDelegate) - .unbind('mouseup.'+this.widgetName, this._mouseUpDelegate); - - if (this._mouseStarted) { - this._mouseStarted = false; - - if (event.target == this._mouseDownEvent.target) { - $.data(event.target, this.widgetName + '.preventClickEvent', true); - } - - this._mouseStop(event); - } - - return false; - }, - - _mouseDistanceMet: function(event) { - return (Math.max( - Math.abs(this._mouseDownEvent.pageX - event.pageX), - Math.abs(this._mouseDownEvent.pageY - event.pageY) - ) >= this.options.distance - ); - }, - - _mouseDelayMet: function(event) { - return this.mouseDelayMet; - }, - - // These are placeholder methods, to be overriden by extending plugin - _mouseStart: function(event) {}, - _mouseDrag: function(event) {}, - _mouseStop: function(event) {}, - _mouseCapture: function(event) { return true; } -}); - -})(jQuery); -/* - * jQuery UI Touch Punch 0.2.2 - * - * Copyright 2011, Dave Furfero - * Dual licensed under the MIT or GPL Version 2 licenses. - * - * Depends: - * jquery.ui.widget.js - * jquery.ui.mouse.js - */ -(function(b){b.support.touch="ontouchend" in document;if(!b.support.touch){return;}var c=b.ui.mouse.prototype,e=c._mouseInit,a;function d(g,h){if(g.originalEvent.touches.length>1){return;}g.preventDefault();var i=g.originalEvent.changedTouches[0],f=document.createEvent("MouseEvents");f.initMouseEvent(h,true,true,window,1,i.screenX,i.screenY,i.clientX,i.clientY,false,false,false,false,0,null);g.target.dispatchEvent(f);}c._touchStart=function(g){var f=this;if(a||!f._mouseCapture(g.originalEvent.changedTouches[0])){return;}a=true;f._touchMoved=false;d(g,"mouseover");d(g,"mousemove");d(g,"mousedown");};c._touchMove=function(f){if(!a){return;}this._touchMoved=true;d(f,"mousemove");};c._touchEnd=function(f){if(!a){return;}d(f,"mouseup");d(f,"mouseout");if(!this._touchMoved){d(f,"click");}a=false;};c._mouseInit=function(){var f=this;f.element.bind("touchstart",b.proxy(f,"_touchStart")).bind("touchmove",b.proxy(f,"_touchMove")).bind("touchend",b.proxy(f,"_touchEnd"));e.call(f);};})(jQuery); - -/*! - * jQuery UI Slider 1.8.23 - * - * Copyright 2012, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://docs.jquery.com/UI/Slider - * - * Depends: - * jquery.ui.core.js - * jquery.ui.mouse.js - * jquery.ui.widget.js - */ -(function( $, undefined ) { - -// number of pages in a slider -// (how many times can you page up/down to go through the whole range) -var numPages = 5; - -$.widget( "ui.slider", $.ui.mouse, { - - widgetEventPrefix: "slide", - - options: { - animate: false, - distance: 0, - max: 100, - min: 0, - orientation: "horizontal", - range: false, - step: 1, - value: 0, - values: null - }, - - _create: function() { - var self = this, - o = this.options, - existingHandles = this.element.find( ".ui-slider-handle" ).addClass( "ui-state-default ui-corner-all" ), - handle = "", - handleCount = ( o.values && o.values.length ) || 1, - handles = []; - - this._keySliding = false; - this._mouseSliding = false; - this._animateOff = true; - this._handleIndex = null; - this._detectOrientation(); - this._mouseInit(); - - this.element - .addClass( "ui-slider" + - " ui-slider-" + this.orientation + - " ui-widget" + - " ui-widget-content" + - " ui-corner-all" + - ( o.disabled ? " ui-slider-disabled ui-disabled" : "" ) ); - - this.range = $([]); - - if ( o.range ) { - if ( o.range === true ) { - if ( !o.values ) { - o.values = [ this._valueMin(), this._valueMin() ]; - } - if ( o.values.length && o.values.length !== 2 ) { - o.values = [ o.values[0], o.values[0] ]; - } - } - - this.range = $( "
" ) - .appendTo( this.element ) - .addClass( "ui-slider-range" + - // note: this isn't the most fittingly semantic framework class for this element, - // but worked best visually with a variety of themes - " ui-widget-header" + - ( ( o.range === "min" || o.range === "max" ) ? " ui-slider-range-" + o.range : "" ) ); - } - - for ( var i = existingHandles.length; i < handleCount; i += 1 ) { - handles.push( handle ); - } - - this.handles = existingHandles.add( $( handles.join( "" ) ).appendTo( self.element ) ); - - this.handle = this.handles.eq( 0 ); - - this.handles.add( this.range ).filter( "a" ) - .click(function( event ) { - event.preventDefault(); - }) - .hover(function() { - if ( !o.disabled ) { - $( this ).addClass( "ui-state-hover" ); - } - }, function() { - $( this ).removeClass( "ui-state-hover" ); - }) - .focus(function() { - if ( !o.disabled ) { - $( ".ui-slider .ui-state-focus" ).removeClass( "ui-state-focus" ); - $( this ).addClass( "ui-state-focus" ); - } else { - $( this ).blur(); - } - }) - .blur(function() { - $( this ).removeClass( "ui-state-focus" ); - }); - - this.handles.each(function( i ) { - $( this ).data( "index.ui-slider-handle", i ); - }); - - this.handles - .keydown(function( event ) { - var index = $( this ).data( "index.ui-slider-handle" ), - allowed, - curVal, - newVal, - step; - - if ( self.options.disabled ) { - return; - } - - switch ( event.keyCode ) { - case $.ui.keyCode.HOME: - case $.ui.keyCode.END: - case $.ui.keyCode.PAGE_UP: - case $.ui.keyCode.PAGE_DOWN: - case $.ui.keyCode.UP: - case $.ui.keyCode.RIGHT: - case $.ui.keyCode.DOWN: - case $.ui.keyCode.LEFT: - event.preventDefault(); - if ( !self._keySliding ) { - self._keySliding = true; - $( this ).addClass( "ui-state-active" ); - allowed = self._start( event, index ); - if ( allowed === false ) { - return; - } - } - break; - } - - step = self.options.step; - if ( self.options.values && self.options.values.length ) { - curVal = newVal = self.values( index ); - } else { - curVal = newVal = self.value(); - } - - switch ( event.keyCode ) { - case $.ui.keyCode.HOME: - newVal = self._valueMin(); - break; - case $.ui.keyCode.END: - newVal = self._valueMax(); - break; - case $.ui.keyCode.PAGE_UP: - newVal = self._trimAlignValue( curVal + ( (self._valueMax() - self._valueMin()) / numPages ) ); - break; - case $.ui.keyCode.PAGE_DOWN: - newVal = self._trimAlignValue( curVal - ( (self._valueMax() - self._valueMin()) / numPages ) ); - break; - case $.ui.keyCode.UP: - case $.ui.keyCode.RIGHT: - if ( curVal === self._valueMax() ) { - return; - } - newVal = self._trimAlignValue( curVal + step ); - break; - case $.ui.keyCode.DOWN: - case $.ui.keyCode.LEFT: - if ( curVal === self._valueMin() ) { - return; - } - newVal = self._trimAlignValue( curVal - step ); - break; - } - - self._slide( event, index, newVal ); - }) - .keyup(function( event ) { - var index = $( this ).data( "index.ui-slider-handle" ); - - if ( self._keySliding ) { - self._keySliding = false; - self._stop( event, index ); - self._change( event, index ); - $( this ).removeClass( "ui-state-active" ); - } - - }); - - this._refreshValue(); - - this._animateOff = false; - }, - - destroy: function() { - this.handles.remove(); - this.range.remove(); - - this.element - .removeClass( "ui-slider" + - " ui-slider-horizontal" + - " ui-slider-vertical" + - " ui-slider-disabled" + - " ui-widget" + - " ui-widget-content" + - " ui-corner-all" ) - .removeData( "slider" ) - .unbind( ".slider" ); - - this._mouseDestroy(); - - return this; - }, - - _mouseCapture: function( event ) { - var o = this.options, - position, - normValue, - distance, - closestHandle, - self, - index, - allowed, - offset, - mouseOverHandle; - - if ( o.disabled ) { - return false; - } - - this.elementSize = { - width: this.element.outerWidth(), - height: this.element.outerHeight() - }; - this.elementOffset = this.element.offset(); - - position = { x: event.pageX, y: event.pageY }; - normValue = this._normValueFromMouse( position ); - distance = this._valueMax() - this._valueMin() + 1; - self = this; - this.handles.each(function( i ) { - var thisDistance = Math.abs( normValue - self.values(i) ); - if ( distance > thisDistance ) { - distance = thisDistance; - closestHandle = $( this ); - index = i; - } - }); - - // workaround for bug #3736 (if both handles of a range are at 0, - // the first is always used as the one with least distance, - // and moving it is obviously prevented by preventing negative ranges) - if( o.range === true && this.values(1) === o.min ) { - index += 1; - closestHandle = $( this.handles[index] ); - } - - allowed = this._start( event, index ); - if ( allowed === false ) { - return false; - } - this._mouseSliding = true; - - self._handleIndex = index; - - closestHandle - .addClass( "ui-state-active" ) - .focus(); - - offset = closestHandle.offset(); - mouseOverHandle = !$( event.target ).parents().andSelf().is( ".ui-slider-handle" ); - this._clickOffset = mouseOverHandle ? { left: 0, top: 0 } : { - left: event.pageX - offset.left - ( closestHandle.width() / 2 ), - top: event.pageY - offset.top - - ( closestHandle.height() / 2 ) - - ( parseInt( closestHandle.css("borderTopWidth"), 10 ) || 0 ) - - ( parseInt( closestHandle.css("borderBottomWidth"), 10 ) || 0) + - ( parseInt( closestHandle.css("marginTop"), 10 ) || 0) - }; - - if ( !this.handles.hasClass( "ui-state-hover" ) ) { - this._slide( event, index, normValue ); - } - this._animateOff = true; - return true; - }, - - _mouseStart: function( event ) { - return true; - }, - - _mouseDrag: function( event ) { - var position = { x: event.pageX, y: event.pageY }, - normValue = this._normValueFromMouse( position ); - - this._slide( event, this._handleIndex, normValue ); - - return false; - }, - - _mouseStop: function( event ) { - this.handles.removeClass( "ui-state-active" ); - this._mouseSliding = false; - - this._stop( event, this._handleIndex ); - this._change( event, this._handleIndex ); - - this._handleIndex = null; - this._clickOffset = null; - this._animateOff = false; - - return false; - }, - - _detectOrientation: function() { - this.orientation = ( this.options.orientation === "vertical" ) ? "vertical" : "horizontal"; - }, - - _normValueFromMouse: function( position ) { - var pixelTotal, - pixelMouse, - percentMouse, - valueTotal, - valueMouse; - - if ( this.orientation === "horizontal" ) { - pixelTotal = this.elementSize.width; - pixelMouse = position.x - this.elementOffset.left - ( this._clickOffset ? this._clickOffset.left : 0 ); - } else { - pixelTotal = this.elementSize.height; - pixelMouse = position.y - this.elementOffset.top - ( this._clickOffset ? this._clickOffset.top : 0 ); - } - - percentMouse = ( pixelMouse / pixelTotal ); - if ( percentMouse > 1 ) { - percentMouse = 1; - } - if ( percentMouse < 0 ) { - percentMouse = 0; - } - if ( this.orientation === "vertical" ) { - percentMouse = 1 - percentMouse; - } - - valueTotal = this._valueMax() - this._valueMin(); - valueMouse = this._valueMin() + percentMouse * valueTotal; - - return this._trimAlignValue( valueMouse ); - }, - - _start: function( event, index ) { - var uiHash = { - handle: this.handles[ index ], - value: this.value() - }; - if ( this.options.values && this.options.values.length ) { - uiHash.value = this.values( index ); - uiHash.values = this.values(); - } - return this._trigger( "start", event, uiHash ); - }, - - _slide: function( event, index, newVal ) { - var otherVal, - newValues, - allowed; - - if ( this.options.values && this.options.values.length ) { - otherVal = this.values( index ? 0 : 1 ); - - if ( ( this.options.values.length === 2 && this.options.range === true ) && - ( ( index === 0 && newVal > otherVal) || ( index === 1 && newVal < otherVal ) ) - ) { - newVal = otherVal; - } - - if ( newVal !== this.values( index ) ) { - newValues = this.values(); - newValues[ index ] = newVal; - // A slide can be canceled by returning false from the slide callback - allowed = this._trigger( "slide", event, { - handle: this.handles[ index ], - value: newVal, - values: newValues - } ); - otherVal = this.values( index ? 0 : 1 ); - if ( allowed !== false ) { - this.values( index, newVal, true ); - } - } - } else { - if ( newVal !== this.value() ) { - // A slide can be canceled by returning false from the slide callback - allowed = this._trigger( "slide", event, { - handle: this.handles[ index ], - value: newVal - } ); - if ( allowed !== false ) { - this.value( newVal ); - } - } - } - }, - - _stop: function( event, index ) { - var uiHash = { - handle: this.handles[ index ], - value: this.value() - }; - if ( this.options.values && this.options.values.length ) { - uiHash.value = this.values( index ); - uiHash.values = this.values(); - } - - this._trigger( "stop", event, uiHash ); - }, - - _change: function( event, index ) { - if ( !this._keySliding && !this._mouseSliding ) { - var uiHash = { - handle: this.handles[ index ], - value: this.value() - }; - if ( this.options.values && this.options.values.length ) { - uiHash.value = this.values( index ); - uiHash.values = this.values(); - } - - this._trigger( "change", event, uiHash ); - } - }, - - value: function( newValue ) { - if ( arguments.length ) { - this.options.value = this._trimAlignValue( newValue ); - this._refreshValue(); - this._change( null, 0 ); - return; - } - - return this._value(); - }, - - values: function( index, newValue ) { - var vals, - newValues, - i; - - if ( arguments.length > 1 ) { - this.options.values[ index ] = this._trimAlignValue( newValue ); - this._refreshValue(); - this._change( null, index ); - return; - } - - if ( arguments.length ) { - if ( $.isArray( arguments[ 0 ] ) ) { - vals = this.options.values; - newValues = arguments[ 0 ]; - for ( i = 0; i < vals.length; i += 1 ) { - vals[ i ] = this._trimAlignValue( newValues[ i ] ); - this._change( null, i ); - } - this._refreshValue(); - } else { - if ( this.options.values && this.options.values.length ) { - return this._values( index ); - } else { - return this.value(); - } - } - } else { - return this._values(); - } - }, - - _setOption: function( key, value ) { - var i, - valsLength = 0; - - if ( $.isArray( this.options.values ) ) { - valsLength = this.options.values.length; - } - - $.Widget.prototype._setOption.apply( this, arguments ); - - switch ( key ) { - case "disabled": - if ( value ) { - this.handles.filter( ".ui-state-focus" ).blur(); - this.handles.removeClass( "ui-state-hover" ); - this.handles.propAttr( "disabled", true ); - this.element.addClass( "ui-disabled" ); - } else { - this.handles.propAttr( "disabled", false ); - this.element.removeClass( "ui-disabled" ); - } - break; - case "orientation": - this._detectOrientation(); - this.element - .removeClass( "ui-slider-horizontal ui-slider-vertical" ) - .addClass( "ui-slider-" + this.orientation ); - this._refreshValue(); - break; - case "value": - this._animateOff = true; - this._refreshValue(); - this._change( null, 0 ); - this._animateOff = false; - break; - case "values": - this._animateOff = true; - this._refreshValue(); - for ( i = 0; i < valsLength; i += 1 ) { - this._change( null, i ); - } - this._animateOff = false; - break; - } - }, - - //internal value getter - // _value() returns value trimmed by min and max, aligned by step - _value: function() { - var val = this.options.value; - val = this._trimAlignValue( val ); - - return val; - }, - - //internal values getter - // _values() returns array of values trimmed by min and max, aligned by step - // _values( index ) returns single value trimmed by min and max, aligned by step - _values: function( index ) { - var val, - vals, - i; - - if ( arguments.length ) { - val = this.options.values[ index ]; - val = this._trimAlignValue( val ); - - return val; - } else { - // .slice() creates a copy of the array - // this copy gets trimmed by min and max and then returned - vals = this.options.values.slice(); - for ( i = 0; i < vals.length; i+= 1) { - vals[ i ] = this._trimAlignValue( vals[ i ] ); - } - - return vals; - } - }, - - // returns the step-aligned value that val is closest to, between (inclusive) min and max - _trimAlignValue: function( val ) { - if ( val <= this._valueMin() ) { - return this._valueMin(); - } - if ( val >= this._valueMax() ) { - return this._valueMax(); - } - var step = ( this.options.step > 0 ) ? this.options.step : 1, - valModStep = (val - this._valueMin()) % step, - alignValue = val - valModStep; - - if ( Math.abs(valModStep) * 2 >= step ) { - alignValue += ( valModStep > 0 ) ? step : ( -step ); - } - - // Since JavaScript has problems with large floats, round - // the final value to 5 digits after the decimal point (see #4124) - return parseFloat( alignValue.toFixed(5) ); - }, - - _valueMin: function() { - return this.options.min; - }, - - _valueMax: function() { - return this.options.max; - }, - - _refreshValue: function() { - var oRange = this.options.range, - o = this.options, - self = this, - animate = ( !this._animateOff ) ? o.animate : false, - valPercent, - _set = {}, - lastValPercent, - value, - valueMin, - valueMax; - - if ( this.options.values && this.options.values.length ) { - this.handles.each(function( i, j ) { - valPercent = ( self.values(i) - self._valueMin() ) / ( self._valueMax() - self._valueMin() ) * 100; - _set[ self.orientation === "horizontal" ? "left" : "bottom" ] = valPercent + "%"; - $( this ).stop( 1, 1 )[ animate ? "animate" : "css" ]( _set, o.animate ); - if ( self.options.range === true ) { - if ( self.orientation === "horizontal" ) { - if ( i === 0 ) { - self.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( { left: valPercent + "%" }, o.animate ); - } - if ( i === 1 ) { - self.range[ animate ? "animate" : "css" ]( { width: ( valPercent - lastValPercent ) + "%" }, { queue: false, duration: o.animate } ); - } - } else { - if ( i === 0 ) { - self.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( { bottom: ( valPercent ) + "%" }, o.animate ); - } - if ( i === 1 ) { - self.range[ animate ? "animate" : "css" ]( { height: ( valPercent - lastValPercent ) + "%" }, { queue: false, duration: o.animate } ); - } - } - } - lastValPercent = valPercent; - }); - } else { - value = this.value(); - valueMin = this._valueMin(); - valueMax = this._valueMax(); - valPercent = ( valueMax !== valueMin ) ? - ( value - valueMin ) / ( valueMax - valueMin ) * 100 : - 0; - _set[ self.orientation === "horizontal" ? "left" : "bottom" ] = valPercent + "%"; - this.handle.stop( 1, 1 )[ animate ? "animate" : "css" ]( _set, o.animate ); - - if ( oRange === "min" && this.orientation === "horizontal" ) { - this.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( { width: valPercent + "%" }, o.animate ); - } - if ( oRange === "max" && this.orientation === "horizontal" ) { - this.range[ animate ? "animate" : "css" ]( { width: ( 100 - valPercent ) + "%" }, { queue: false, duration: o.animate } ); - } - if ( oRange === "min" && this.orientation === "vertical" ) { - this.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( { height: valPercent + "%" }, o.animate ); - } - if ( oRange === "max" && this.orientation === "vertical" ) { - this.range[ animate ? "animate" : "css" ]( { height: ( 100 - valPercent ) + "%" }, { queue: false, duration: o.animate } ); - } - } - } - -}); - -$.extend( $.ui.slider, { - version: "1.8.23" -}); - -}(jQuery)); - -cartodb.moduleLoad('torque', torque); - -Profiler = cartodb.core.Profiler - diff --git a/vendor/assets/javascripts/cartodb.uncompressed.js b/vendor/assets/javascripts/cartodb.uncompressed.js deleted file mode 100644 index a630e7ba44..0000000000 --- a/vendor/assets/javascripts/cartodb.uncompressed.js +++ /dev/null @@ -1,35782 +0,0 @@ -// cartodb.js version: 3.11.24-dev -// uncompressed version: cartodb.uncompressed.js -// sha: 82a854637c4c8c671044109794c3ae400d1ae71b -(function() { - var root = this; - - if(!true) { - if(root.jQuery === undefined) { - throw "jQuery should be loaded before include cartodb.js"; - } - } - - // save current libraries - var __prev = { - jQuery: root.jQuery, - $: root.$, - L: root.L, - Mustache: root.Mustache, - Backbone: root.Backbone, - _: root._ - }; - - - /*! jQuery v1.7.2 jquery.com | jquery.org/license */ -(function(a,b){function cy(a){return f.isWindow(a)?a:a.nodeType===9?a.defaultView||a.parentWindow:!1}function cu(a){if(!cj[a]){var b=c.body,d=f("<"+a+">").appendTo(b),e=d.css("display");d.remove();if(e==="none"||e===""){ck||(ck=c.createElement("iframe"),ck.frameBorder=ck.width=ck.height=0),b.appendChild(ck);if(!cl||!ck.createElement)cl=(ck.contentWindow||ck.contentDocument).document,cl.write((f.support.boxModel?"":"")+""),cl.close();d=cl.createElement(a),cl.body.appendChild(d),e=f.css(d,"display"),b.removeChild(ck)}cj[a]=e}return cj[a]}function ct(a,b){var c={};f.each(cp.concat.apply([],cp.slice(0,b)),function(){c[this]=a});return c}function cs(){cq=b}function cr(){setTimeout(cs,0);return cq=f.now()}function ci(){try{return new a.ActiveXObject("Microsoft.XMLHTTP")}catch(b){}}function ch(){try{return new a.XMLHttpRequest}catch(b){}}function cb(a,c){a.dataFilter&&(c=a.dataFilter(c,a.dataType));var d=a.dataTypes,e={},g,h,i=d.length,j,k=d[0],l,m,n,o,p;for(g=1;g0){if(c!=="border")for(;e=0===c})}function S(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function K(){return!0}function J(){return!1}function n(a,b,c){var d=b+"defer",e=b+"queue",g=b+"mark",h=f._data(a,d);h&&(c==="queue"||!f._data(a,e))&&(c==="mark"||!f._data(a,g))&&setTimeout(function(){!f._data(a,e)&&!f._data(a,g)&&(f.removeData(a,d,!0),h.fire())},0)}function m(a){for(var b in a){if(b==="data"&&f.isEmptyObject(a[b]))continue;if(b!=="toJSON")return!1}return!0}function l(a,c,d){if(d===b&&a.nodeType===1){var e="data-"+c.replace(k,"-$1").toLowerCase();d=a.getAttribute(e);if(typeof d=="string"){try{d=d==="true"?!0:d==="false"?!1:d==="null"?null:f.isNumeric(d)?+d:j.test(d)?f.parseJSON(d):d}catch(g){}f.data(a,c,d)}else d=b}return d}function h(a){var b=g[a]={},c,d;a=a.split(/\s+/);for(c=0,d=a.length;c)[^>]*$|#([\w\-]*)$)/,j=/\S/,k=/^\s+/,l=/\s+$/,m=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,n=/^[\],:{}\s]*$/,o=/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,p=/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,q=/(?:^|:|,)(?:\s*\[)+/g,r=/(webkit)[ \/]([\w.]+)/,s=/(opera)(?:.*version)?[ \/]([\w.]+)/,t=/(msie) ([\w.]+)/,u=/(mozilla)(?:.*? rv:([\w.]+))?/,v=/-([a-z]|[0-9])/ig,w=/^-ms-/,x=function(a,b){return(b+"").toUpperCase()},y=d.userAgent,z,A,B,C=Object.prototype.toString,D=Object.prototype.hasOwnProperty,E=Array.prototype.push,F=Array.prototype.slice,G=String.prototype.trim,H=Array.prototype.indexOf,I={};e.fn=e.prototype={constructor:e,init:function(a,d,f){var g,h,j,k;if(!a)return this;if(a.nodeType){this.context=this[0]=a,this.length=1;return this}if(a==="body"&&!d&&c.body){this.context=c,this[0]=c.body,this.selector=a,this.length=1;return this}if(typeof a=="string"){a.charAt(0)!=="<"||a.charAt(a.length-1)!==">"||a.length<3?g=i.exec(a):g=[null,a,null];if(g&&(g[1]||!d)){if(g[1]){d=d instanceof e?d[0]:d,k=d?d.ownerDocument||d:c,j=m.exec(a),j?e.isPlainObject(d)?(a=[c.createElement(j[1])],e.fn.attr.call(a,d,!0)):a=[k.createElement(j[1])]:(j=e.buildFragment([g[1]],[k]),a=(j.cacheable?e.clone(j.fragment):j.fragment).childNodes);return e.merge(this,a)}h=c.getElementById(g[2]);if(h&&h.parentNode){if(h.id!==g[2])return f.find(a);this.length=1,this[0]=h}this.context=c,this.selector=a;return this}return!d||d.jquery?(d||f).find(a):this.constructor(d).find(a)}if(e.isFunction(a))return f.ready(a);a.selector!==b&&(this.selector=a.selector,this.context=a.context);return e.makeArray(a,this)},selector:"",jquery:"1.7.2",length:0,size:function(){return this.length},toArray:function(){return F.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this[this.length+a]:this[a]},pushStack:function(a,b,c){var d=this.constructor();e.isArray(a)?E.apply(d,a):e.merge(d,a),d.prevObject=this,d.context=this.context,b==="find"?d.selector=this.selector+(this.selector?" ":"")+c:b&&(d.selector=this.selector+"."+b+"("+c+")");return d},each:function(a,b){return e.each(this,a,b)},ready:function(a){e.bindReady(),A.add(a);return this},eq:function(a){a=+a;return a===-1?this.slice(a):this.slice(a,a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(F.apply(this,arguments),"slice",F.call(arguments).join(","))},map:function(a){return this.pushStack(e.map(this,function(b,c){return a.call(b,c,b)}))},end:function(){return this.prevObject||this.constructor(null)},push:E,sort:[].sort,splice:[].splice},e.fn.init.prototype=e.fn,e.extend=e.fn.extend=function(){var a,c,d,f,g,h,i=arguments[0]||{},j=1,k=arguments.length,l=!1;typeof i=="boolean"&&(l=i,i=arguments[1]||{},j=2),typeof i!="object"&&!e.isFunction(i)&&(i={}),k===j&&(i=this,--j);for(;j0)return;A.fireWith(c,[e]),e.fn.trigger&&e(c).trigger("ready").off("ready")}},bindReady:function(){if(!A){A=e.Callbacks("once memory");if(c.readyState==="complete")return setTimeout(e.ready,1);if(c.addEventListener)c.addEventListener("DOMContentLoaded",B,!1),a.addEventListener("load",e.ready,!1);else if(c.attachEvent){c.attachEvent("onreadystatechange",B),a.attachEvent("onload",e.ready);var b=!1;try{b=a.frameElement==null}catch(d){}c.documentElement.doScroll&&b&&J()}}},isFunction:function(a){return e.type(a)==="function"},isArray:Array.isArray||function(a){return e.type(a)==="array"},isWindow:function(a){return a!=null&&a==a.window},isNumeric:function(a){return!isNaN(parseFloat(a))&&isFinite(a)},type:function(a){return a==null?String(a):I[C.call(a)]||"object"},isPlainObject:function(a){if(!a||e.type(a)!=="object"||a.nodeType||e.isWindow(a))return!1;try{if(a.constructor&&!D.call(a,"constructor")&&!D.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}var d;for(d in a);return d===b||D.call(a,d)},isEmptyObject:function(a){for(var b in a)return!1;return!0},error:function(a){throw new Error(a)},parseJSON:function(b){if(typeof b!="string"||!b)return null;b=e.trim(b);if(a.JSON&&a.JSON.parse)return a.JSON.parse(b);if(n.test(b.replace(o,"@").replace(p,"]").replace(q,"")))return(new Function("return "+b))();e.error("Invalid JSON: "+b)},parseXML:function(c){if(typeof c!="string"||!c)return null;var d,f;try{a.DOMParser?(f=new DOMParser,d=f.parseFromString(c,"text/xml")):(d=new ActiveXObject("Microsoft.XMLDOM"),d.async="false",d.loadXML(c))}catch(g){d=b}(!d||!d.documentElement||d.getElementsByTagName("parsererror").length)&&e.error("Invalid XML: "+c);return d},noop:function(){},globalEval:function(b){b&&j.test(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(w,"ms-").replace(v,x)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,c,d){var f,g=0,h=a.length,i=h===b||e.isFunction(a);if(d){if(i){for(f in a)if(c.apply(a[f],d)===!1)break}else for(;g0&&a[0]&&a[j-1]||j===0||e.isArray(a));if(k)for(;i1?i.call(arguments,0):b,j.notifyWith(k,e)}}function l(a){return function(c){b[a]=arguments.length>1?i.call(arguments,0):c,--g||j.resolveWith(j,b)}}var b=i.call(arguments,0),c=0,d=b.length,e=Array(d),g=d,h=d,j=d<=1&&a&&f.isFunction(a.promise)?a:f.Deferred(),k=j.promise();if(d>1){for(;c
a",d=p.getElementsByTagName("*"),e=p.getElementsByTagName("a")[0];if(!d||!d.length||!e)return{};g=c.createElement("select"),h=g.appendChild(c.createElement("option")),i=p.getElementsByTagName("input")[0],b={leadingWhitespace:p.firstChild.nodeType===3,tbody:!p.getElementsByTagName("tbody").length,htmlSerialize:!!p.getElementsByTagName("link").length,style:/top/.test(e.getAttribute("style")),hrefNormalized:e.getAttribute("href")==="/a",opacity:/^0.55/.test(e.style.opacity),cssFloat:!!e.style.cssFloat,checkOn:i.value==="on",optSelected:h.selected,getSetAttribute:p.className!=="t",enctype:!!c.createElement("form").enctype,html5Clone:c.createElement("nav").cloneNode(!0).outerHTML!=="<:nav>",submitBubbles:!0,changeBubbles:!0,focusinBubbles:!1,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0,pixelMargin:!0},f.boxModel=b.boxModel=c.compatMode==="CSS1Compat",i.checked=!0,b.noCloneChecked=i.cloneNode(!0).checked,g.disabled=!0,b.optDisabled=!h.disabled;try{delete p.test}catch(r){b.deleteExpando=!1}!p.addEventListener&&p.attachEvent&&p.fireEvent&&(p.attachEvent("onclick",function(){b.noCloneEvent=!1}),p.cloneNode(!0).fireEvent("onclick")),i=c.createElement("input"),i.value="t",i.setAttribute("type","radio"),b.radioValue=i.value==="t",i.setAttribute("checked","checked"),i.setAttribute("name","t"),p.appendChild(i),j=c.createDocumentFragment(),j.appendChild(p.lastChild),b.checkClone=j.cloneNode(!0).cloneNode(!0).lastChild.checked,b.appendChecked=i.checked,j.removeChild(i),j.appendChild(p);if(p.attachEvent)for(n in{submit:1,change:1,focusin:1})m="on"+n,o=m in p,o||(p.setAttribute(m,"return;"),o=typeof p[m]=="function"),b[n+"Bubbles"]=o;j.removeChild(p),j=g=h=p=i=null,f(function(){var d,e,g,h,i,j,l,m,n,q,r,s,t,u=c.getElementsByTagName("body")[0];!u||(m=1,t="padding:0;margin:0;border:",r="position:absolute;top:0;left:0;width:1px;height:1px;",s=t+"0;visibility:hidden;",n="style='"+r+t+"5px solid #000;",q="
"+""+"
",d=c.createElement("div"),d.style.cssText=s+"width:0;height:0;position:static;top:0;margin-top:"+m+"px",u.insertBefore(d,u.firstChild),p=c.createElement("div"),d.appendChild(p),p.innerHTML="
t
",k=p.getElementsByTagName("td"),o=k[0].offsetHeight===0,k[0].style.display="",k[1].style.display="none",b.reliableHiddenOffsets=o&&k[0].offsetHeight===0,a.getComputedStyle&&(p.innerHTML="",l=c.createElement("div"),l.style.width="0",l.style.marginRight="0",p.style.width="2px",p.appendChild(l),b.reliableMarginRight=(parseInt((a.getComputedStyle(l,null)||{marginRight:0}).marginRight,10)||0)===0),typeof p.style.zoom!="undefined"&&(p.innerHTML="",p.style.width=p.style.padding="1px",p.style.border=0,p.style.overflow="hidden",p.style.display="inline",p.style.zoom=1,b.inlineBlockNeedsLayout=p.offsetWidth===3,p.style.display="block",p.style.overflow="visible",p.innerHTML="
",b.shrinkWrapBlocks=p.offsetWidth!==3),p.style.cssText=r+s,p.innerHTML=q,e=p.firstChild,g=e.firstChild,i=e.nextSibling.firstChild.firstChild,j={doesNotAddBorder:g.offsetTop!==5,doesAddBorderForTableAndCells:i.offsetTop===5},g.style.position="fixed",g.style.top="20px",j.fixedPosition=g.offsetTop===20||g.offsetTop===15,g.style.position=g.style.top="",e.style.overflow="hidden",e.style.position="relative",j.subtractsBorderForOverflowNotVisible=g.offsetTop===-5,j.doesNotIncludeMarginInBodyOffset=u.offsetTop!==m,a.getComputedStyle&&(p.style.marginTop="1%",b.pixelMargin=(a.getComputedStyle(p,null)||{marginTop:0}).marginTop!=="1%"),typeof d.style.zoom!="undefined"&&(d.style.zoom=1),u.removeChild(d),l=p=d=null,f.extend(b,j))});return b}();var j=/^(?:\{.*\}|\[.*\])$/,k=/([A-Z])/g;f.extend({cache:{},uuid:0,expando:"jQuery"+(f.fn.jquery+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(a){a=a.nodeType?f.cache[a[f.expando]]:a[f.expando];return!!a&&!m(a)},data:function(a,c,d,e){if(!!f.acceptData(a)){var g,h,i,j=f.expando,k=typeof c=="string",l=a.nodeType,m=l?f.cache:a,n=l?a[j]:a[j]&&j,o=c==="events";if((!n||!m[n]||!o&&!e&&!m[n].data)&&k&&d===b)return;n||(l?a[j]=n=++f.uuid:n=j),m[n]||(m[n]={},l||(m[n].toJSON=f.noop));if(typeof c=="object"||typeof c=="function")e?m[n]=f.extend(m[n],c):m[n].data=f.extend(m[n].data,c);g=h=m[n],e||(h.data||(h.data={}),h=h.data),d!==b&&(h[f.camelCase(c)]=d);if(o&&!h[c])return g.events;k?(i=h[c],i==null&&(i=h[f.camelCase(c)])):i=h;return i}},removeData:function(a,b,c){if(!!f.acceptData(a)){var d,e,g,h=f.expando,i=a.nodeType,j=i?f.cache:a,k=i?a[h]:h;if(!j[k])return;if(b){d=c?j[k]:j[k].data;if(d){f.isArray(b)||(b in d?b=[b]:(b=f.camelCase(b),b in d?b=[b]:b=b.split(" ")));for(e=0,g=b.length;e1,null,!1)},removeData:function(a){return this.each(function(){f.removeData(this,a)})}}),f.extend({_mark:function(a,b){a&&(b=(b||"fx")+"mark",f._data(a,b,(f._data(a,b)||0)+1))},_unmark:function(a,b,c){a!==!0&&(c=b,b=a,a=!1);if(b){c=c||"fx";var d=c+"mark",e=a?0:(f._data(b,d)||1)-1;e?f._data(b,d,e):(f.removeData(b,d,!0),n(b,c,"mark"))}},queue:function(a,b,c){var d;if(a){b=(b||"fx")+"queue",d=f._data(a,b),c&&(!d||f.isArray(c)?d=f._data(a,b,f.makeArray(c)):d.push(c));return d||[]}},dequeue:function(a,b){b=b||"fx";var c=f.queue(a,b),d=c.shift(),e={};d==="inprogress"&&(d=c.shift()),d&&(b==="fx"&&c.unshift("inprogress"),f._data(a,b+".run",e),d.call(a,function(){f.dequeue(a,b)},e)),c.length||(f.removeData(a,b+"queue "+b+".run",!0),n(a,b,"queue"))}}),f.fn.extend({queue:function(a,c){var d=2;typeof a!="string"&&(c=a,a="fx",d--);if(arguments.length1)},removeAttr:function(a){return this.each(function(){f.removeAttr(this,a)})},prop:function(a,b){return f.access(this,f.prop,a,b,arguments.length>1)},removeProp:function(a){a=f.propFix[a]||a;return this.each(function(){try{this[a]=b,delete this[a]}catch(c){}})},addClass:function(a){var b,c,d,e,g,h,i;if(f.isFunction(a))return this.each(function(b){f(this).addClass(a.call(this,b,this.className))});if(a&&typeof a=="string"){b=a.split(p);for(c=0,d=this.length;c-1)return!0;return!1},val:function(a){var c,d,e,g=this[0];{if(!!arguments.length){e=f.isFunction(a);return this.each(function(d){var g=f(this),h;if(this.nodeType===1){e?h=a.call(this,d,g.val()):h=a,h==null?h="":typeof h=="number"?h+="":f.isArray(h)&&(h=f.map(h,function(a){return a==null?"":a+""})),c=f.valHooks[this.type]||f.valHooks[this.nodeName.toLowerCase()];if(!c||!("set"in c)||c.set(this,h,"value")===b)this.value=h}})}if(g){c=f.valHooks[g.type]||f.valHooks[g.nodeName.toLowerCase()];if(c&&"get"in c&&(d=c.get(g,"value"))!==b)return d;d=g.value;return typeof d=="string"?d.replace(q,""):d==null?"":d}}}}),f.extend({valHooks:{option:{get:function(a){var b=a.attributes.value;return!b||b.specified?a.value:a.text}},select:{get:function(a){var b,c,d,e,g=a.selectedIndex,h=[],i=a.options,j=a.type==="select-one";if(g<0)return null;c=j?g:0,d=j?g+1:i.length;for(;c=0}),c.length||(a.selectedIndex=-1);return c}}},attrFn:{val:!0,css:!0,html:!0,text:!0,data:!0,width:!0,height:!0,offset:!0},attr:function(a,c,d,e){var g,h,i,j=a.nodeType;if(!!a&&j!==3&&j!==8&&j!==2){if(e&&c in f.attrFn)return f(a)[c](d);if(typeof a.getAttribute=="undefined")return f.prop(a,c,d);i=j!==1||!f.isXMLDoc(a),i&&(c=c.toLowerCase(),h=f.attrHooks[c]||(u.test(c)?x:w));if(d!==b){if(d===null){f.removeAttr(a,c);return}if(h&&"set"in h&&i&&(g=h.set(a,d,c))!==b)return g;a.setAttribute(c,""+d);return d}if(h&&"get"in h&&i&&(g=h.get(a,c))!==null)return g;g=a.getAttribute(c);return g===null?b:g}},removeAttr:function(a,b){var c,d,e,g,h,i=0;if(b&&a.nodeType===1){d=b.toLowerCase().split(p),g=d.length;for(;i=0}})});var z=/^(?:textarea|input|select)$/i,A=/^([^\.]*)?(?:\.(.+))?$/,B=/(?:^|\s)hover(\.\S+)?\b/,C=/^key/,D=/^(?:mouse|contextmenu)|click/,E=/^(?:focusinfocus|focusoutblur)$/,F=/^(\w*)(?:#([\w\-]+))?(?:\.([\w\-]+))?$/,G=function( -a){var b=F.exec(a);b&&(b[1]=(b[1]||"").toLowerCase(),b[3]=b[3]&&new RegExp("(?:^|\\s)"+b[3]+"(?:\\s|$)"));return b},H=function(a,b){var c=a.attributes||{};return(!b[1]||a.nodeName.toLowerCase()===b[1])&&(!b[2]||(c.id||{}).value===b[2])&&(!b[3]||b[3].test((c["class"]||{}).value))},I=function(a){return f.event.special.hover?a:a.replace(B,"mouseenter$1 mouseleave$1")};f.event={add:function(a,c,d,e,g){var h,i,j,k,l,m,n,o,p,q,r,s;if(!(a.nodeType===3||a.nodeType===8||!c||!d||!(h=f._data(a)))){d.handler&&(p=d,d=p.handler,g=p.selector),d.guid||(d.guid=f.guid++),j=h.events,j||(h.events=j={}),i=h.handle,i||(h.handle=i=function(a){return typeof f!="undefined"&&(!a||f.event.triggered!==a.type)?f.event.dispatch.apply(i.elem,arguments):b},i.elem=a),c=f.trim(I(c)).split(" ");for(k=0;k=0&&(h=h.slice(0,-1),k=!0),h.indexOf(".")>=0&&(i=h.split("."),h=i.shift(),i.sort());if((!e||f.event.customEvent[h])&&!f.event.global[h])return;c=typeof c=="object"?c[f.expando]?c:new f.Event(h,c):new f.Event(h),c.type=h,c.isTrigger=!0,c.exclusive=k,c.namespace=i.join("."),c.namespace_re=c.namespace?new RegExp("(^|\\.)"+i.join("\\.(?:.*\\.)?")+"(\\.|$)"):null,o=h.indexOf(":")<0?"on"+h:"";if(!e){j=f.cache;for(l in j)j[l].events&&j[l].events[h]&&f.event.trigger(c,d,j[l].handle.elem,!0);return}c.result=b,c.target||(c.target=e),d=d!=null?f.makeArray(d):[],d.unshift(c),p=f.event.special[h]||{};if(p.trigger&&p.trigger.apply(e,d)===!1)return;r=[[e,p.bindType||h]];if(!g&&!p.noBubble&&!f.isWindow(e)){s=p.delegateType||h,m=E.test(s+h)?e:e.parentNode,n=null;for(;m;m=m.parentNode)r.push([m,s]),n=m;n&&n===e.ownerDocument&&r.push([n.defaultView||n.parentWindow||a,s])}for(l=0;le&&j.push({elem:this,matches:d.slice(e)});for(k=0;k0?this.on(b,null,a,c):this.trigger(b)},f.attrFn&&(f.attrFn[b]=!0),C.test(b)&&(f.event.fixHooks[b]=f.event.keyHooks),D.test(b)&&(f.event.fixHooks[b]=f.event.mouseHooks)}),function(){function x(a,b,c,e,f,g){for(var h=0,i=e.length;h0){k=j;break}}j=j[a]}e[h]=k}}}function w(a,b,c,e,f,g){for(var h=0,i=e.length;h+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,d="sizcache"+(Math.random()+"").replace(".",""),e=0,g=Object.prototype.toString,h=!1,i=!0,j=/\\/g,k=/\r\n/g,l=/\W/;[0,0].sort(function(){i=!1;return 0});var m=function(b,d,e,f){e=e||[],d=d||c;var h=d;if(d.nodeType!==1&&d.nodeType!==9)return[];if(!b||typeof b!="string")return e;var i,j,k,l,n,q,r,t,u=!0,v=m.isXML(d),w=[],x=b;do{a.exec(""),i=a.exec(x);if(i){x=i[3],w.push(i[1]);if(i[2]){l=i[3];break}}}while(i);if(w.length>1&&p.exec(b))if(w.length===2&&o.relative[w[0]])j=y(w[0]+w[1],d,f);else{j=o.relative[w[0]]?[d]:m(w.shift(),d);while(w.length)b=w.shift(),o.relative[b]&&(b+=w.shift()),j=y(b,j,f)}else{!f&&w.length>1&&d.nodeType===9&&!v&&o.match.ID.test(w[0])&&!o.match.ID.test(w[w.length-1])&&(n=m.find(w.shift(),d,v),d=n.expr?m.filter(n.expr,n.set)[0]:n.set[0]);if(d){n=f?{expr:w.pop(),set:s(f)}:m.find(w.pop(),w.length===1&&(w[0]==="~"||w[0]==="+")&&d.parentNode?d.parentNode:d,v),j=n.expr?m.filter(n.expr,n.set):n.set,w.length>0?k=s(j):u=!1;while(w.length)q=w.pop(),r=q,o.relative[q]?r=w.pop():q="",r==null&&(r=d),o.relative[q](k,r,v)}else k=w=[]}k||(k=j),k||m.error(q||b);if(g.call(k)==="[object Array]")if(!u)e.push.apply(e,k);else if(d&&d.nodeType===1)for(t=0;k[t]!=null;t++)k[t]&&(k[t]===!0||k[t].nodeType===1&&m.contains(d,k[t]))&&e.push(j[t]);else for(t=0;k[t]!=null;t++)k[t]&&k[t].nodeType===1&&e.push(j[t]);else s(k,e);l&&(m(l,h,e,f),m.uniqueSort(e));return e};m.uniqueSort=function(a){if(u){h=i,a.sort(u);if(h)for(var b=1;b0},m.find=function(a,b,c){var d,e,f,g,h,i;if(!a)return[];for(e=0,f=o.order.length;e":function(a,b){var c,d=typeof b=="string",e=0,f=a.length;if(d&&!l.test(b)){b=b.toLowerCase();for(;e=0)?c||d.push(h):c&&(b[g]=!1));return!1},ID:function(a){return a[1].replace(j,"")},TAG:function(a,b){return a[1].replace(j,"").toLowerCase()},CHILD:function(a){if(a[1]==="nth"){a[2]||m.error(a[0]),a[2]=a[2].replace(/^\+|\s*/g,"");var b=/(-?)(\d*)(?:n([+\-]?\d*))?/.exec(a[2]==="even"&&"2n"||a[2]==="odd"&&"2n+1"||!/\D/.test(a[2])&&"0n+"+a[2]||a[2]);a[2]=b[1]+(b[2]||1)-0,a[3]=b[3]-0}else a[2]&&m.error(a[0]);a[0]=e++;return a},ATTR:function(a,b,c,d,e,f){var g=a[1]=a[1].replace(j,"");!f&&o.attrMap[g]&&(a[1]=o.attrMap[g]),a[4]=(a[4]||a[5]||"").replace(j,""),a[2]==="~="&&(a[4]=" "+a[4]+" ");return a},PSEUDO:function(b,c,d,e,f){if(b[1]==="not")if((a.exec(b[3])||"").length>1||/^\w/.test(b[3]))b[3]=m(b[3],null,null,c);else{var g=m.filter(b[3],c,d,!0^f);d||e.push.apply(e,g);return!1}else if(o.match.POS.test(b[0])||o.match.CHILD.test(b[0]))return!0;return b},POS:function(a){a.unshift(!0);return a}},filters:{enabled:function(a){return a.disabled===!1&&a.type!=="hidden"},disabled:function(a){return a.disabled===!0},checked:function(a){return a.checked===!0},selected:function(a){a.parentNode&&a.parentNode.selectedIndex;return a.selected===!0},parent:function(a){return!!a.firstChild},empty:function(a){return!a.firstChild},has:function(a,b,c){return!!m(c[3],a).length},header:function(a){return/h\d/i.test(a.nodeName)},text:function(a){var b=a.getAttribute("type"),c=a.type;return a.nodeName.toLowerCase()==="input"&&"text"===c&&(b===c||b===null)},radio:function(a){return a.nodeName.toLowerCase()==="input"&&"radio"===a.type},checkbox:function(a){return a.nodeName.toLowerCase()==="input"&&"checkbox"===a.type},file:function(a){return a.nodeName.toLowerCase()==="input"&&"file"===a.type},password:function(a){return a.nodeName.toLowerCase()==="input"&&"password"===a.type},submit:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"submit"===a.type},image:function(a){return a.nodeName.toLowerCase()==="input"&&"image"===a.type},reset:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"reset"===a.type},button:function(a){var b=a.nodeName.toLowerCase();return b==="input"&&"button"===a.type||b==="button"},input:function(a){return/input|select|textarea|button/i.test(a.nodeName)},focus:function(a){return a===a.ownerDocument.activeElement}},setFilters:{first:function(a,b){return b===0},last:function(a,b,c,d){return b===d.length-1},even:function(a,b){return b%2===0},odd:function(a,b){return b%2===1},lt:function(a,b,c){return bc[3]-0},nth:function(a,b,c){return c[3]-0===b},eq:function(a,b,c){return c[3]-0===b}},filter:{PSEUDO:function(a,b,c,d){var e=b[1],f=o.filters[e];if(f)return f(a,c,b,d);if(e==="contains")return(a.textContent||a.innerText||n([a])||"").indexOf(b[3])>=0;if(e==="not"){var g=b[3];for(var h=0,i=g.length;h=0}},ID:function(a,b){return a.nodeType===1&&a.getAttribute("id")===b},TAG:function(a,b){return b==="*"&&a.nodeType===1||!!a.nodeName&&a.nodeName.toLowerCase()===b},CLASS:function(a,b){return(" "+(a.className||a.getAttribute("class"))+" ").indexOf(b)>-1},ATTR:function(a,b){var c=b[1],d=m.attr?m.attr(a,c):o.attrHandle[c]?o.attrHandle[c](a):a[c]!=null?a[c]:a.getAttribute(c),e=d+"",f=b[2],g=b[4];return d==null?f==="!=":!f&&m.attr?d!=null:f==="="?e===g:f==="*="?e.indexOf(g)>=0:f==="~="?(" "+e+" ").indexOf(g)>=0:g?f==="!="?e!==g:f==="^="?e.indexOf(g)===0:f==="$="?e.substr(e.length-g.length)===g:f==="|="?e===g||e.substr(0,g.length+1)===g+"-":!1:e&&d!==!1},POS:function(a,b,c,d){var e=b[2],f=o.setFilters[e];if(f)return f(a,c,b,d)}}},p=o.match.POS,q=function(a,b){return"\\"+(b-0+1)};for(var r in o.match)o.match[r]=new RegExp(o.match[r].source+/(?![^\[]*\])(?![^\(]*\))/.source),o.leftMatch[r]=new RegExp(/(^(?:.|\r|\n)*?)/.source+o.match[r].source.replace(/\\(\d+)/g,q));o.match.globalPOS=p;var s=function(a,b){a=Array.prototype.slice.call(a,0);if(b){b.push.apply(b,a);return b}return a};try{Array.prototype.slice.call(c.documentElement.childNodes,0)[0].nodeType}catch(t){s=function(a,b){var c=0,d=b||[];if(g.call(a)==="[object Array]")Array.prototype.push.apply(d,a);else if(typeof a.length=="number")for(var e=a.length;c",e.insertBefore(a,e.firstChild),c.getElementById(d)&&(o.find.ID=function(a,c,d){if(typeof c.getElementById!="undefined"&&!d){var e=c.getElementById(a[1]);return e?e.id===a[1]||typeof e.getAttributeNode!="undefined"&&e.getAttributeNode("id").nodeValue===a[1]?[e]:b:[]}},o.filter.ID=function(a,b){var c=typeof a.getAttributeNode!="undefined"&&a.getAttributeNode("id");return a.nodeType===1&&c&&c.nodeValue===b}),e.removeChild(a),e=a=null}(),function(){var a=c.createElement("div");a.appendChild(c.createComment("")),a.getElementsByTagName("*").length>0&&(o.find.TAG=function(a,b){var c=b.getElementsByTagName(a[1]);if(a[1]==="*"){var d=[];for(var e=0;c[e];e++)c[e].nodeType===1&&d.push(c[e]);c=d}return c}),a.innerHTML="",a.firstChild&&typeof a.firstChild.getAttribute!="undefined"&&a.firstChild.getAttribute("href")!=="#"&&(o.attrHandle.href=function(a){return a.getAttribute("href",2)}),a=null}(),c.querySelectorAll&&function(){var a=m,b=c.createElement("div"),d="__sizzle__";b.innerHTML="

";if(!b.querySelectorAll||b.querySelectorAll(".TEST").length!==0){m=function(b,e,f,g){e=e||c;if(!g&&!m.isXML(e)){var h=/^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec(b);if(h&&(e.nodeType===1||e.nodeType===9)){if(h[1])return s(e.getElementsByTagName(b),f);if(h[2]&&o.find.CLASS&&e.getElementsByClassName)return s(e.getElementsByClassName(h[2]),f)}if(e.nodeType===9){if(b==="body"&&e.body)return s([e.body],f);if(h&&h[3]){var i=e.getElementById(h[3]);if(!i||!i.parentNode)return s([],f);if(i.id===h[3])return s([i],f)}try{return s(e.querySelectorAll(b),f)}catch(j){}}else if(e.nodeType===1&&e.nodeName.toLowerCase()!=="object"){var k=e,l=e.getAttribute("id"),n=l||d,p=e.parentNode,q=/^\s*[+~]/.test(b);l?n=n.replace(/'/g,"\\$&"):e.setAttribute("id",n),q&&p&&(e=e.parentNode);try{if(!q||p)return s(e.querySelectorAll("[id='"+n+"'] "+b),f)}catch(r){}finally{l||k.removeAttribute("id")}}}return a(b,e,f,g)};for(var e in a)m[e]=a[e];b=null}}(),function(){var a=c.documentElement,b=a.matchesSelector||a.mozMatchesSelector||a.webkitMatchesSelector||a.msMatchesSelector;if(b){var d=!b.call(c.createElement("div"),"div"),e=!1;try{b.call(c.documentElement,"[test!='']:sizzle")}catch(f){e=!0}m.matchesSelector=function(a,c){c=c.replace(/\=\s*([^'"\]]*)\s*\]/g,"='$1']");if(!m.isXML(a))try{if(e||!o.match.PSEUDO.test(c)&&!/!=/.test(c)){var f=b.call(a,c);if(f||!d||a.document&&a.document.nodeType!==11)return f}}catch(g){}return m(c,null,null,[a]).length>0}}}(),function(){var a=c.createElement("div");a.innerHTML="
";if(!!a.getElementsByClassName&&a.getElementsByClassName("e").length!==0){a.lastChild.className="e";if(a.getElementsByClassName("e").length===1)return;o.order.splice(1,0,"CLASS"),o.find.CLASS=function(a,b,c){if(typeof b.getElementsByClassName!="undefined"&&!c)return b.getElementsByClassName(a[1])},a=null}}(),c.documentElement.contains?m.contains=function(a,b){return a!==b&&(a.contains?a.contains(b):!0)}:c.documentElement.compareDocumentPosition?m.contains=function(a,b){return!!(a.compareDocumentPosition(b)&16)}:m.contains=function(){return!1},m.isXML=function(a){var b=(a?a.ownerDocument||a:0).documentElement;return b?b.nodeName!=="HTML":!1};var y=function(a,b,c){var d,e=[],f="",g=b.nodeType?[b]:b;while(d=o.match.PSEUDO.exec(a))f+=d[0],a=a.replace(o.match.PSEUDO,"");a=o.relative[a]?a+"*":a;for(var h=0,i=g.length;h0)for(h=g;h=0:f.filter(a,this).length>0:this.filter(a).length>0)},closest:function(a,b){var c=[],d,e,g=this[0];if(f.isArray(a)){var h=1;while(g&&g.ownerDocument&&g!==b){for(d=0;d-1:f.find.matchesSelector(g,a)){c.push(g);break}g=g.parentNode;if(!g||!g.ownerDocument||g===b||g.nodeType===11)break}}c=c.length>1?f.unique(c):c;return this.pushStack(c,"closest",a)},index:function(a){if(!a)return this[0]&&this[0].parentNode?this.prevAll().length:-1;if(typeof a=="string")return f.inArray(this[0],f(a));return f.inArray(a.jquery?a[0]:a,this)},add:function(a,b){var c=typeof a=="string"?f(a,b):f.makeArray(a&&a.nodeType?[a]:a),d=f.merge(this.get(),c);return this.pushStack(S(c[0])||S(d[0])?d:f.unique(d))},andSelf:function(){return this.add(this.prevObject)}}),f.each({parent:function(a){var b=a.parentNode;return b&&b.nodeType!==11?b:null},parents:function(a){return f.dir(a,"parentNode")},parentsUntil:function(a,b,c){return f.dir(a,"parentNode",c)},next:function(a){return f.nth(a,2,"nextSibling")},prev:function(a){return f.nth(a,2,"previousSibling")},nextAll:function(a){return f.dir(a,"nextSibling")},prevAll:function(a){return f.dir(a,"previousSibling")},nextUntil:function(a,b,c){return f.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return f.dir(a,"previousSibling",c)},siblings:function(a){return f.sibling((a.parentNode||{}).firstChild,a)},children:function(a){return f.sibling(a.firstChild)},contents:function(a){return f.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:f.makeArray(a.childNodes)}},function(a,b){f.fn[a]=function(c,d){var e=f.map(this,b,c);L.test(a)||(d=c),d&&typeof d=="string"&&(e=f.filter(d,e)),e=this.length>1&&!R[a]?f.unique(e):e,(this.length>1||N.test(d))&&M.test(a)&&(e=e.reverse());return this.pushStack(e,a,P.call(arguments).join(","))}}),f.extend({filter:function(a,b,c){c&&(a=":not("+a+")");return b.length===1?f.find.matchesSelector(b[0],a)?[b[0]]:[]:f.find.matches(a,b)},dir:function(a,c,d){var e=[],g=a[c];while(g&&g.nodeType!==9&&(d===b||g.nodeType!==1||!f(g).is(d)))g.nodeType===1&&e.push(g),g=g[c];return e},nth:function(a,b,c,d){b=b||1;var e=0;for(;a;a=a[c])if(a.nodeType===1&&++e===b)break;return a},sibling:function(a,b){var c=[];for(;a;a=a.nextSibling)a.nodeType===1&&a!==b&&c.push(a);return c}});var V="abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",W=/ jQuery\d+="(?:\d+|null)"/g,X=/^\s+/,Y=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,Z=/<([\w:]+)/,$=/]","i"),bd=/checked\s*(?:[^=]|=\s*.checked.)/i,be=/\/(java|ecma)script/i,bf=/^\s*",""],legend:[1,"
","
"],thead:[1,"","
"],tr:[2,"","
"],td:[3,"","
"],col:[2,"","
"],area:[1,"",""],_default:[0,"",""]},bh=U(c);bg.optgroup=bg.option,bg.tbody=bg.tfoot=bg.colgroup=bg.caption=bg.thead,bg.th=bg.td,f.support.htmlSerialize||(bg._default=[1,"div
","
"]),f.fn.extend({text:function(a){return f.access(this,function(a){return a===b?f.text(this):this.empty().append((this[0]&&this[0].ownerDocument||c).createTextNode(a))},null,a,arguments.length)},wrapAll:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapAll(a.call(this,b))});if(this[0]){var b=f(a,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstChild&&a.firstChild.nodeType===1)a=a.firstChild;return a}).append(this)}return this},wrapInner:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapInner(a.call(this,b))});return this.each(function(){var b=f(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){var b=f.isFunction(a);return this.each(function(c){f(this).wrapAll(b?a.call(this,c):a)})},unwrap:function(){return this.parent().each(function(){f.nodeName(this,"body")||f(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.appendChild(a)})},prepend:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this)});if(arguments.length){var a=f -.clean(arguments);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this.nextSibling)});if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,f.clean(arguments));return a}},remove:function(a,b){for(var c=0,d;(d=this[c])!=null;c++)if(!a||f.filter(a,[d]).length)!b&&d.nodeType===1&&(f.cleanData(d.getElementsByTagName("*")),f.cleanData([d])),d.parentNode&&d.parentNode.removeChild(d);return this},empty:function(){for(var a=0,b;(b=this[a])!=null;a++){b.nodeType===1&&f.cleanData(b.getElementsByTagName("*"));while(b.firstChild)b.removeChild(b.firstChild)}return this},clone:function(a,b){a=a==null?!1:a,b=b==null?a:b;return this.map(function(){return f.clone(this,a,b)})},html:function(a){return f.access(this,function(a){var c=this[0]||{},d=0,e=this.length;if(a===b)return c.nodeType===1?c.innerHTML.replace(W,""):null;if(typeof a=="string"&&!ba.test(a)&&(f.support.leadingWhitespace||!X.test(a))&&!bg[(Z.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(Y,"<$1>");try{for(;d1&&l0?this.clone(!0):this).get();f(e[h])[b](j),d=d.concat(j)}return this.pushStack(d,a,e.selector)}}),f.extend({clone:function(a,b,c){var d,e,g,h=f.support.html5Clone||f.isXMLDoc(a)||!bc.test("<"+a.nodeName+">")?a.cloneNode(!0):bo(a);if((!f.support.noCloneEvent||!f.support.noCloneChecked)&&(a.nodeType===1||a.nodeType===11)&&!f.isXMLDoc(a)){bk(a,h),d=bl(a),e=bl(h);for(g=0;d[g];++g)e[g]&&bk(d[g],e[g])}if(b){bj(a,h);if(c){d=bl(a),e=bl(h);for(g=0;d[g];++g)bj(d[g],e[g])}}d=e=null;return h},clean:function(a,b,d,e){var g,h,i,j=[];b=b||c,typeof b.createElement=="undefined"&&(b=b.ownerDocument||b[0]&&b[0].ownerDocument||c);for(var k=0,l;(l=a[k])!=null;k++){typeof l=="number"&&(l+="");if(!l)continue;if(typeof l=="string")if(!_.test(l))l=b.createTextNode(l);else{l=l.replace(Y,"<$1>");var m=(Z.exec(l)||["",""])[1].toLowerCase(),n=bg[m]||bg._default,o=n[0],p=b.createElement("div"),q=bh.childNodes,r;b===c?bh.appendChild(p):U(b).appendChild(p),p.innerHTML=n[1]+l+n[2];while(o--)p=p.lastChild;if(!f.support.tbody){var s=$.test(l),t=m==="table"&&!s?p.firstChild&&p.firstChild.childNodes:n[1]===""&&!s?p.childNodes:[];for(i=t.length-1;i>=0;--i)f.nodeName(t[i],"tbody")&&!t[i].childNodes.length&&t[i].parentNode.removeChild(t[i])}!f.support.leadingWhitespace&&X.test(l)&&p.insertBefore(b.createTextNode(X.exec(l)[0]),p.firstChild),l=p.childNodes,p&&(p.parentNode.removeChild(p),q.length>0&&(r=q[q.length-1],r&&r.parentNode&&r.parentNode.removeChild(r)))}var u;if(!f.support.appendChecked)if(l[0]&&typeof (u=l.length)=="number")for(i=0;i1)},f.extend({cssHooks:{opacity:{get:function(a,b){if(b){var c=by(a,"opacity");return c===""?"1":c}return a.style.opacity}}},cssNumber:{fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":f.support.cssFloat?"cssFloat":"styleFloat"},style:function(a,c,d,e){if(!!a&&a.nodeType!==3&&a.nodeType!==8&&!!a.style){var g,h,i=f.camelCase(c),j=a.style,k=f.cssHooks[i];c=f.cssProps[i]||i;if(d===b){if(k&&"get"in k&&(g=k.get(a,!1,e))!==b)return g;return j[c]}h=typeof d,h==="string"&&(g=bu.exec(d))&&(d=+(g[1]+1)*+g[2]+parseFloat(f.css(a,c)),h="number");if(d==null||h==="number"&&isNaN(d))return;h==="number"&&!f.cssNumber[i]&&(d+="px");if(!k||!("set"in k)||(d=k.set(a,d))!==b)try{j[c]=d}catch(l){}}},css:function(a,c,d){var e,g;c=f.camelCase(c),g=f.cssHooks[c],c=f.cssProps[c]||c,c==="cssFloat"&&(c="float");if(g&&"get"in g&&(e=g.get(a,!0,d))!==b)return e;if(by)return by(a,c)},swap:function(a,b,c){var d={},e,f;for(f in b)d[f]=a.style[f],a.style[f]=b[f];e=c.call(a);for(f in b)a.style[f]=d[f];return e}}),f.curCSS=f.css,c.defaultView&&c.defaultView.getComputedStyle&&(bz=function(a,b){var c,d,e,g,h=a.style;b=b.replace(br,"-$1").toLowerCase(),(d=a.ownerDocument.defaultView)&&(e=d.getComputedStyle(a,null))&&(c=e.getPropertyValue(b),c===""&&!f.contains(a.ownerDocument.documentElement,a)&&(c=f.style(a,b))),!f.support.pixelMargin&&e&&bv.test(b)&&bt.test(c)&&(g=h.width,h.width=c,c=e.width,h.width=g);return c}),c.documentElement.currentStyle&&(bA=function(a,b){var c,d,e,f=a.currentStyle&&a.currentStyle[b],g=a.style;f==null&&g&&(e=g[b])&&(f=e),bt.test(f)&&(c=g.left,d=a.runtimeStyle&&a.runtimeStyle.left,d&&(a.runtimeStyle.left=a.currentStyle.left),g.left=b==="fontSize"?"1em":f,f=g.pixelLeft+"px",g.left=c,d&&(a.runtimeStyle.left=d));return f===""?"auto":f}),by=bz||bA,f.each(["height","width"],function(a,b){f.cssHooks[b]={get:function(a,c,d){if(c)return a.offsetWidth!==0?bB(a,b,d):f.swap(a,bw,function(){return bB(a,b,d)})},set:function(a,b){return bs.test(b)?b+"px":b}}}),f.support.opacity||(f.cssHooks.opacity={get:function(a,b){return bq.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?parseFloat(RegExp.$1)/100+"":b?"1":""},set:function(a,b){var c=a.style,d=a.currentStyle,e=f.isNumeric(b)?"alpha(opacity="+b*100+")":"",g=d&&d.filter||c.filter||"";c.zoom=1;if(b>=1&&f.trim(g.replace(bp,""))===""){c.removeAttribute("filter");if(d&&!d.filter)return}c.filter=bp.test(g)?g.replace(bp,e):g+" "+e}}),f(function(){f.support.reliableMarginRight||(f.cssHooks.marginRight={get:function(a,b){return f.swap(a,{display:"inline-block"},function(){return b?by(a,"margin-right"):a.style.marginRight})}})}),f.expr&&f.expr.filters&&(f.expr.filters.hidden=function(a){var b=a.offsetWidth,c=a.offsetHeight;return b===0&&c===0||!f.support.reliableHiddenOffsets&&(a.style&&a.style.display||f.css(a,"display"))==="none"},f.expr.filters.visible=function(a){return!f.expr.filters.hidden(a)}),f.each({margin:"",padding:"",border:"Width"},function(a,b){f.cssHooks[a+b]={expand:function(c){var d,e=typeof c=="string"?c.split(" "):[c],f={};for(d=0;d<4;d++)f[a+bx[d]+b]=e[d]||e[d-2]||e[0];return f}}});var bC=/%20/g,bD=/\[\]$/,bE=/\r?\n/g,bF=/#.*$/,bG=/^(.*?):[ \t]*([^\r\n]*)\r?$/mg,bH=/^(?:color|date|datetime|datetime-local|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,bI=/^(?:about|app|app\-storage|.+\-extension|file|res|widget):$/,bJ=/^(?:GET|HEAD)$/,bK=/^\/\//,bL=/\?/,bM=/)<[^<]*)*<\/script>/gi,bN=/^(?:select|textarea)/i,bO=/\s+/,bP=/([?&])_=[^&]*/,bQ=/^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+))?)?/,bR=f.fn.load,bS={},bT={},bU,bV,bW=["*/"]+["*"];try{bU=e.href}catch(bX){bU=c.createElement("a"),bU.href="",bU=bU.href}bV=bQ.exec(bU.toLowerCase())||[],f.fn.extend({load:function(a,c,d){if(typeof a!="string"&&bR)return bR.apply(this,arguments);if(!this.length)return this;var e=a.indexOf(" ");if(e>=0){var g=a.slice(e,a.length);a=a.slice(0,e)}var h="GET";c&&(f.isFunction(c)?(d=c,c=b):typeof c=="object"&&(c=f.param(c,f.ajaxSettings.traditional),h="POST"));var i=this;f.ajax({url:a,type:h,dataType:"html",data:c,complete:function(a,b,c){c=a.responseText,a.isResolved()&&(a.done(function(a){c=a}),i.html(g?f("
").append(c.replace(bM,"")).find(g):c)),d&&i.each(d,[c,b,a])}});return this},serialize:function(){return f.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?f.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||bN.test(this.nodeName)||bH.test(this.type))}).map(function(a,b){var c=f(this).val();return c==null?null:f.isArray(c)?f.map(c,function(a,c){return{name:b.name,value:a.replace(bE,"\r\n")}}):{name:b.name,value:c.replace(bE,"\r\n")}}).get()}}),f.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(a,b){f.fn[b]=function(a){return this.on(b,a)}}),f.each(["get","post"],function(a,c){f[c]=function(a,d,e,g){f.isFunction(d)&&(g=g||e,e=d,d=b);return f.ajax({type:c,url:a,data:d,success:e,dataType:g})}}),f.extend({getScript:function(a,c){return f.get(a,b,c,"script")},getJSON:function(a,b,c){return f.get(a,b,c,"json")},ajaxSetup:function(a,b){b?b$(a,f.ajaxSettings):(b=a,a=f.ajaxSettings),b$(a,b);return a},ajaxSettings:{url:bU,isLocal:bI.test(bV[1]),global:!0,type:"GET",contentType:"application/x-www-form-urlencoded; charset=UTF-8",processData:!0,async:!0,accepts:{xml:"application/xml, text/xml",html:"text/html",text:"text/plain",json:"application/json, text/javascript","*":bW},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText"},converters:{"* text":a.String,"text html":!0,"text json":f.parseJSON,"text xml":f.parseXML},flatOptions:{context:!0,url:!0}},ajaxPrefilter:bY(bS),ajaxTransport:bY(bT),ajax:function(a,c){function w(a,c,l,m){if(s!==2){s=2,q&&clearTimeout(q),p=b,n=m||"",v.readyState=a>0?4:0;var o,r,u,w=c,x=l?ca(d,v,l):b,y,z;if(a>=200&&a<300||a===304){if(d.ifModified){if(y=v.getResponseHeader("Last-Modified"))f.lastModified[k]=y;if(z=v.getResponseHeader("Etag"))f.etag[k]=z}if(a===304)w="notmodified",o=!0;else try{r=cb(d,x),w="success",o=!0}catch(A){w="parsererror",u=A}}else{u=w;if(!w||a)w="error",a<0&&(a=0)}v.status=a,v.statusText=""+(c||w),o?h.resolveWith(e,[r,w,v]):h.rejectWith(e,[v,w,u]),v.statusCode(j),j=b,t&&g.trigger("ajax"+(o?"Success":"Error"),[v,d,o?r:u]),i.fireWith(e,[v,w]),t&&(g.trigger("ajaxComplete",[v,d]),--f.active||f.event.trigger("ajaxStop"))}}typeof a=="object"&&(c=a,a=b),c=c||{};var d=f.ajaxSetup({},c),e=d.context||d,g=e!==d&&(e.nodeType||e instanceof f)?f(e):f.event,h=f.Deferred(),i=f.Callbacks("once memory"),j=d.statusCode||{},k,l={},m={},n,o,p,q,r,s=0,t,u,v={readyState:0,setRequestHeader:function(a,b){if(!s){var c=a.toLowerCase();a=m[c]=m[c]||a,l[a]=b}return this},getAllResponseHeaders:function(){return s===2?n:null},getResponseHeader:function(a){var c;if(s===2){if(!o){o={};while(c=bG.exec(n))o[c[1].toLowerCase()]=c[2]}c=o[a.toLowerCase()]}return c===b?null:c},overrideMimeType:function(a){s||(d.mimeType=a);return this},abort:function(a){a=a||"abort",p&&p.abort(a),w(0,a);return this}};h.promise(v),v.success=v.done,v.error=v.fail,v.complete=i.add,v.statusCode=function(a){if(a){var b;if(s<2)for(b in a)j[b]=[j[b],a[b]];else b=a[v.status],v.then(b,b)}return this},d.url=((a||d.url)+"").replace(bF,"").replace(bK,bV[1]+"//"),d.dataTypes=f.trim(d.dataType||"*").toLowerCase().split(bO),d.crossDomain==null&&(r=bQ.exec(d.url.toLowerCase()),d.crossDomain=!(!r||r[1]==bV[1]&&r[2]==bV[2]&&(r[3]||(r[1]==="http:"?80:443))==(bV[3]||(bV[1]==="http:"?80:443)))),d.data&&d.processData&&typeof d.data!="string"&&(d.data=f.param(d.data,d.traditional)),bZ(bS,d,c,v);if(s===2)return!1;t=d.global,d.type=d.type.toUpperCase(),d.hasContent=!bJ.test(d.type),t&&f.active++===0&&f.event.trigger("ajaxStart");if(!d.hasContent){d.data&&(d.url+=(bL.test(d.url)?"&":"?")+d.data,delete d.data),k=d.url;if(d.cache===!1){var x=f.now(),y=d.url.replace(bP,"$1_="+x);d.url=y+(y===d.url?(bL.test(d.url)?"&":"?")+"_="+x:"")}}(d.data&&d.hasContent&&d.contentType!==!1||c.contentType)&&v.setRequestHeader("Content-Type",d.contentType),d.ifModified&&(k=k||d.url,f.lastModified[k]&&v.setRequestHeader("If-Modified-Since",f.lastModified[k]),f.etag[k]&&v.setRequestHeader("If-None-Match",f.etag[k])),v.setRequestHeader("Accept",d.dataTypes[0]&&d.accepts[d.dataTypes[0]]?d.accepts[d.dataTypes[0]]+(d.dataTypes[0]!=="*"?", "+bW+"; q=0.01":""):d.accepts["*"]);for(u in d.headers)v.setRequestHeader(u,d.headers[u]);if(d.beforeSend&&(d.beforeSend.call(e,v,d)===!1||s===2)){v.abort();return!1}for(u in{success:1,error:1,complete:1})v[u](d[u]);p=bZ(bT,d,c,v);if(!p)w(-1,"No Transport");else{v.readyState=1,t&&g.trigger("ajaxSend",[v,d]),d.async&&d.timeout>0&&(q=setTimeout(function(){v.abort("timeout")},d.timeout));try{s=1,p.send(l,w)}catch(z){if(s<2)w(-1,z);else throw z}}return v},param:function(a,c){var d=[],e=function(a,b){b=f.isFunction(b)?b():b,d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(b)};c===b&&(c=f.ajaxSettings.traditional);if(f.isArray(a)||a.jquery&&!f.isPlainObject(a))f.each(a,function(){e(this.name,this.value)});else for(var g in a)b_(g,a[g],c,e);return d.join("&").replace(bC,"+")}}),f.extend({active:0,lastModified:{},etag:{}});var cc=f.now(),cd=/(\=)\?(&|$)|\?\?/i;f.ajaxSetup({jsonp:"callback",jsonpCallback:function(){return f.expando+"_"+cc++}}),f.ajaxPrefilter("json jsonp",function(b,c,d){var e=typeof b.data=="string"&&/^application\/x\-www\-form\-urlencoded/.test(b.contentType);if(b.dataTypes[0]==="jsonp"||b.jsonp!==!1&&(cd.test(b.url)||e&&cd.test(b.data))){var g,h=b.jsonpCallback=f.isFunction(b.jsonpCallback)?b.jsonpCallback():b.jsonpCallback,i=a[h],j=b.url,k=b.data,l="$1"+h+"$2";b.jsonp!==!1&&(j=j.replace(cd,l),b.url===j&&(e&&(k=k.replace(cd,l)),b.data===k&&(j+=(/\?/.test(j)?"&":"?")+b.jsonp+"="+h))),b.url=j,b.data=k,a[h]=function(a){g=[a]},d.always(function(){a[h]=i,g&&f.isFunction(i)&&a[h](g[0])}),b.converters["script json"]=function(){g||f.error(h+" was not called");return g[0]},b.dataTypes[0]="json";return"script"}}),f.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/javascript|ecmascript/},converters:{"text script":function(a){f.globalEval(a);return a}}}),f.ajaxPrefilter("script",function(a){a.cache===b&&(a.cache=!1),a.crossDomain&&(a.type="GET",a.global=!1)}),f.ajaxTransport("script",function(a){if(a.crossDomain){var d,e=c.head||c.getElementsByTagName("head")[0]||c.documentElement;return{send:function(f,g){d=c.createElement("script"),d.async="async",a.scriptCharset&&(d.charset=a.scriptCharset),d.src=a.url,d.onload=d.onreadystatechange=function(a,c){if(c||!d.readyState||/loaded|complete/.test(d.readyState))d.onload=d.onreadystatechange=null,e&&d.parentNode&&e.removeChild(d),d=b,c||g(200,"success")},e.insertBefore(d,e.firstChild)},abort:function(){d&&d.onload(0,1)}}}});var ce=a.ActiveXObject?function(){for(var a in cg)cg[a](0,1)}:!1,cf=0,cg;f.ajaxSettings.xhr=a.ActiveXObject?function(){return!this.isLocal&&ch()||ci()}:ch,function(a){f.extend(f.support,{ajax:!!a,cors:!!a&&"withCredentials"in a})}(f.ajaxSettings.xhr()),f.support.ajax&&f.ajaxTransport(function(c){if(!c.crossDomain||f.support.cors){var d;return{send:function(e,g){var h=c.xhr(),i,j;c.username?h.open(c.type,c.url,c.async,c.username,c.password):h.open(c.type,c.url,c.async);if(c.xhrFields)for(j in c.xhrFields)h[j]=c.xhrFields[j];c.mimeType&&h.overrideMimeType&&h.overrideMimeType(c.mimeType),!c.crossDomain&&!e["X-Requested-With"]&&(e["X-Requested-With"]="XMLHttpRequest");try{for(j in e)h.setRequestHeader(j,e[j])}catch(k){}h.send(c.hasContent&&c.data||null),d=function(a,e){var j,k,l,m,n;try{if(d&&(e||h.readyState===4)){d=b,i&&(h.onreadystatechange=f.noop,ce&&delete cg[i]);if(e)h.readyState!==4&&h.abort();else{j=h.status,l=h.getAllResponseHeaders(),m={},n=h.responseXML,n&&n.documentElement&&(m.xml=n);try{m.text=h.responseText}catch(a){}try{k=h.statusText}catch(o){k=""}!j&&c.isLocal&&!c.crossDomain?j=m.text?200:404:j===1223&&(j=204)}}}catch(p){e||g(-1,p)}m&&g(j,k,m,l)},!c.async||h.readyState===4?d():(i=++cf,ce&&(cg||(cg={},f(a).unload(ce)),cg[i]=d),h.onreadystatechange=d)},abort:function(){d&&d(0,1)}}}});var cj={},ck,cl,cm=/^(?:toggle|show|hide)$/,cn=/^([+\-]=)?([\d+.\-]+)([a-z%]*)$/i,co,cp=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]],cq;f.fn.extend({show:function(a,b,c){var d,e;if(a||a===0)return this.animate(ct("show",3),a,b,c);for(var g=0,h=this.length;g=i.duration+this.startTime){this.now=this.end,this.pos=this.state=1,this.update(),i.animatedProperties[this.prop]=!0;for(b in i.animatedProperties)i.animatedProperties[b]!==!0&&(g=!1);if(g){i.overflow!=null&&!f.support.shrinkWrapBlocks&&f.each(["","X","Y"],function(a,b){h.style["overflow"+b]=i.overflow[a]}),i.hide&&f(h).hide();if(i.hide||i.show)for(b in i.animatedProperties)f.style(h,b,i.orig[b]),f.removeData(h,"fxshow"+b,!0),f.removeData(h,"toggle"+b,!0);d=i.complete,d&&(i.complete=!1,d.call(h))}return!1}i.duration==Infinity?this.now=e:(c=e-this.startTime,this.state=c/i.duration,this.pos=f.easing[i.animatedProperties[this.prop]](this.state,c,0,1,i.duration),this.now=this.start+(this.end-this.start)*this.pos),this.update();return!0}},f.extend(f.fx,{tick:function(){var a,b=f.timers,c=0;for(;c-1,k={},l={},m,n;j?(l=e.position(),m=l.top,n=l.left):(m=parseFloat(h)||0,n=parseFloat(i)||0),f.isFunction(b)&&(b=b.call(a,c,g)),b.top!=null&&(k.top=b.top-g.top+m),b.left!=null&&(k.left=b.left-g.left+n),"using"in b?b.using.call(a,k):e.css(k)}},f.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),c=this.offset(),d=cx.test(b[0].nodeName)?{top:0,left:0}:b.offset();c.top-=parseFloat(f.css(a,"marginTop"))||0,c.left-=parseFloat(f.css(a,"marginLeft"))||0,d.top+=parseFloat(f.css(b[0],"borderTopWidth"))||0,d.left+=parseFloat(f.css(b[0],"borderLeftWidth"))||0;return{top:c.top-d.top,left:c.left-d.left}},offsetParent:function(){return this.map(function(){var a=this.offsetParent||c.body;while(a&&!cx.test(a.nodeName)&&f.css(a,"position")==="static")a=a.offsetParent;return a})}}),f.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(a,c){var d=/Y/.test(c);f.fn[a]=function(e){return f.access(this,function(a,e,g){var h=cy(a);if(g===b)return h?c in h?h[c]:f.support.boxModel&&h.document.documentElement[e]||h.document.body[e]:a[e];h?h.scrollTo(d?f(h).scrollLeft():g,d?g:f(h).scrollTop()):a[e]=g},a,e,arguments.length,null)}}),f.each({Height:"height",Width:"width"},function(a,c){var d="client"+a,e="scroll"+a,g="offset"+a;f.fn["inner"+a]=function(){var a=this[0];return a?a.style?parseFloat(f.css(a,c,"padding")):this[c]():null},f.fn["outer"+a]=function(a){var b=this[0];return b?b.style?parseFloat(f.css(b,c,a?"margin":"border")):this[c]():null},f.fn[c]=function(a){return f.access(this,function(a,c,h){var i,j,k,l;if(f.isWindow(a)){i=a.document,j=i.documentElement[d];return f.support.boxModel&&j||i.body&&i.body[d]||j}if(a.nodeType===9){i=a.documentElement;if(i[d]>=i[e])return i[d];return Math.max(a.body[e],i[e],a.body[g],i[g])}if(h===b){k=f.css(a,c),l=parseFloat(k);return f.isNumeric(l)?l:k}f(a).css(c,h)},c,a,arguments.length,null)}}),a.jQuery=a.$=f,typeof define=="function"&&define.amd&&define.amd.jQuery&&define("jquery",[],function(){return f})})(window);(function(){var n=this,t=n._,r={},e=Array.prototype,u=Object.prototype,i=Function.prototype,a=e.push,o=e.slice,c=e.concat,l=u.toString,f=u.hasOwnProperty,s=e.forEach,p=e.map,h=e.reduce,v=e.reduceRight,d=e.filter,g=e.every,m=e.some,y=e.indexOf,b=e.lastIndexOf,x=Array.isArray,_=Object.keys,j=i.bind,w=function(n){return n instanceof w?n:this instanceof w?(this._wrapped=n,void 0):new w(n)};"undefined"!=typeof exports?("undefined"!=typeof module&&module.exports&&(exports=module.exports=w),exports._=w):n._=w,w.VERSION="1.4.4";var A=w.each=w.forEach=function(n,t,e){if(null!=n)if(s&&n.forEach===s)n.forEach(t,e);else if(n.length===+n.length){for(var u=0,i=n.length;i>u;u++)if(t.call(e,n[u],u,n)===r)return}else for(var a in n)if(w.has(n,a)&&t.call(e,n[a],a,n)===r)return};w.map=w.collect=function(n,t,r){var e=[];return null==n?e:p&&n.map===p?n.map(t,r):(A(n,function(n,u,i){e[e.length]=t.call(r,n,u,i)}),e)};var O="Reduce of empty array with no initial value";w.reduce=w.foldl=w.inject=function(n,t,r,e){var u=arguments.length>2;if(null==n&&(n=[]),h&&n.reduce===h)return e&&(t=w.bind(t,e)),u?n.reduce(t,r):n.reduce(t);if(A(n,function(n,i,a){u?r=t.call(e,r,n,i,a):(r=n,u=!0)}),!u)throw new TypeError(O);return r},w.reduceRight=w.foldr=function(n,t,r,e){var u=arguments.length>2;if(null==n&&(n=[]),v&&n.reduceRight===v)return e&&(t=w.bind(t,e)),u?n.reduceRight(t,r):n.reduceRight(t);var i=n.length;if(i!==+i){var a=w.keys(n);i=a.length}if(A(n,function(o,c,l){c=a?a[--i]:--i,u?r=t.call(e,r,n[c],c,l):(r=n[c],u=!0)}),!u)throw new TypeError(O);return r},w.find=w.detect=function(n,t,r){var e;return E(n,function(n,u,i){return t.call(r,n,u,i)?(e=n,!0):void 0}),e},w.filter=w.select=function(n,t,r){var e=[];return null==n?e:d&&n.filter===d?n.filter(t,r):(A(n,function(n,u,i){t.call(r,n,u,i)&&(e[e.length]=n)}),e)},w.reject=function(n,t,r){return w.filter(n,function(n,e,u){return!t.call(r,n,e,u)},r)},w.every=w.all=function(n,t,e){t||(t=w.identity);var u=!0;return null==n?u:g&&n.every===g?n.every(t,e):(A(n,function(n,i,a){return(u=u&&t.call(e,n,i,a))?void 0:r}),!!u)};var E=w.some=w.any=function(n,t,e){t||(t=w.identity);var u=!1;return null==n?u:m&&n.some===m?n.some(t,e):(A(n,function(n,i,a){return u||(u=t.call(e,n,i,a))?r:void 0}),!!u)};w.contains=w.include=function(n,t){return null==n?!1:y&&n.indexOf===y?n.indexOf(t)!=-1:E(n,function(n){return n===t})},w.invoke=function(n,t){var r=o.call(arguments,2),e=w.isFunction(t);return w.map(n,function(n){return(e?t:n[t]).apply(n,r)})},w.pluck=function(n,t){return w.map(n,function(n){return n[t]})},w.where=function(n,t,r){return w.isEmpty(t)?r?null:[]:w[r?"find":"filter"](n,function(n){for(var r in t)if(t[r]!==n[r])return!1;return!0})},w.findWhere=function(n,t){return w.where(n,t,!0)},w.max=function(n,t,r){if(!t&&w.isArray(n)&&n[0]===+n[0]&&65535>n.length)return Math.max.apply(Math,n);if(!t&&w.isEmpty(n))return-1/0;var e={computed:-1/0,value:-1/0};return A(n,function(n,u,i){var a=t?t.call(r,n,u,i):n;a>=e.computed&&(e={value:n,computed:a})}),e.value},w.min=function(n,t,r){if(!t&&w.isArray(n)&&n[0]===+n[0]&&65535>n.length)return Math.min.apply(Math,n);if(!t&&w.isEmpty(n))return 1/0;var e={computed:1/0,value:1/0};return A(n,function(n,u,i){var a=t?t.call(r,n,u,i):n;e.computed>a&&(e={value:n,computed:a})}),e.value},w.shuffle=function(n){var t,r=0,e=[];return A(n,function(n){t=w.random(r++),e[r-1]=e[t],e[t]=n}),e};var k=function(n){return w.isFunction(n)?n:function(t){return t[n]}};w.sortBy=function(n,t,r){var e=k(t);return w.pluck(w.map(n,function(n,t,u){return{value:n,index:t,criteria:e.call(r,n,t,u)}}).sort(function(n,t){var r=n.criteria,e=t.criteria;if(r!==e){if(r>e||r===void 0)return 1;if(e>r||e===void 0)return-1}return n.indexi;){var o=i+a>>>1;u>r.call(e,n[o])?i=o+1:a=o}return i},w.toArray=function(n){return n?w.isArray(n)?o.call(n):n.length===+n.length?w.map(n,w.identity):w.values(n):[]},w.size=function(n){return null==n?0:n.length===+n.length?n.length:w.keys(n).length},w.first=w.head=w.take=function(n,t,r){return null==n?void 0:null==t||r?n[0]:o.call(n,0,t)},w.initial=function(n,t,r){return o.call(n,0,n.length-(null==t||r?1:t))},w.last=function(n,t,r){return null==n?void 0:null==t||r?n[n.length-1]:o.call(n,Math.max(n.length-t,0))},w.rest=w.tail=w.drop=function(n,t,r){return o.call(n,null==t||r?1:t)},w.compact=function(n){return w.filter(n,w.identity)};var R=function(n,t,r){return A(n,function(n){w.isArray(n)?t?a.apply(r,n):R(n,t,r):r.push(n)}),r};w.flatten=function(n,t){return R(n,t,[])},w.without=function(n){return w.difference(n,o.call(arguments,1))},w.uniq=w.unique=function(n,t,r,e){w.isFunction(t)&&(e=r,r=t,t=!1);var u=r?w.map(n,r,e):n,i=[],a=[];return A(u,function(r,e){(t?e&&a[a.length-1]===r:w.contains(a,r))||(a.push(r),i.push(n[e]))}),i},w.union=function(){return w.uniq(c.apply(e,arguments))},w.intersection=function(n){var t=o.call(arguments,1);return w.filter(w.uniq(n),function(n){return w.every(t,function(t){return w.indexOf(t,n)>=0})})},w.difference=function(n){var t=c.apply(e,o.call(arguments,1));return w.filter(n,function(n){return!w.contains(t,n)})},w.zip=function(){for(var n=o.call(arguments),t=w.max(w.pluck(n,"length")),r=Array(t),e=0;t>e;e++)r[e]=w.pluck(n,""+e);return r},w.object=function(n,t){if(null==n)return{};for(var r={},e=0,u=n.length;u>e;e++)t?r[n[e]]=t[e]:r[n[e][0]]=n[e][1];return r},w.indexOf=function(n,t,r){if(null==n)return-1;var e=0,u=n.length;if(r){if("number"!=typeof r)return e=w.sortedIndex(n,t),n[e]===t?e:-1;e=0>r?Math.max(0,u+r):r}if(y&&n.indexOf===y)return n.indexOf(t,r);for(;u>e;e++)if(n[e]===t)return e;return-1},w.lastIndexOf=function(n,t,r){if(null==n)return-1;var e=null!=r;if(b&&n.lastIndexOf===b)return e?n.lastIndexOf(t,r):n.lastIndexOf(t);for(var u=e?r:n.length;u--;)if(n[u]===t)return u;return-1},w.range=function(n,t,r){1>=arguments.length&&(t=n||0,n=0),r=arguments[2]||1;for(var e=Math.max(Math.ceil((t-n)/r),0),u=0,i=Array(e);e>u;)i[u++]=n,n+=r;return i},w.bind=function(n,t){if(n.bind===j&&j)return j.apply(n,o.call(arguments,1));var r=o.call(arguments,2);return function(){return n.apply(t,r.concat(o.call(arguments)))}},w.partial=function(n){var t=o.call(arguments,1);return function(){return n.apply(this,t.concat(o.call(arguments)))}},w.bindAll=function(n){var t=o.call(arguments,1);return 0===t.length&&(t=w.functions(n)),A(t,function(t){n[t]=w.bind(n[t],n)}),n},w.memoize=function(n,t){var r={};return t||(t=w.identity),function(){var e=t.apply(this,arguments);return w.has(r,e)?r[e]:r[e]=n.apply(this,arguments)}},w.delay=function(n,t){var r=o.call(arguments,2);return setTimeout(function(){return n.apply(null,r)},t)},w.defer=function(n){return w.delay.apply(w,[n,1].concat(o.call(arguments,1)))},w.throttle=function(n,t){var r,e,u,i,a=0,o=function(){a=new Date,u=null,i=n.apply(r,e)};return function(){var c=new Date,l=t-(c-a);return r=this,e=arguments,0>=l?(clearTimeout(u),u=null,a=c,i=n.apply(r,e)):u||(u=setTimeout(o,l)),i}},w.debounce=function(n,t,r){var e,u;return function(){var i=this,a=arguments,o=function(){e=null,r||(u=n.apply(i,a))},c=r&&!e;return clearTimeout(e),e=setTimeout(o,t),c&&(u=n.apply(i,a)),u}},w.once=function(n){var t,r=!1;return function(){return r?t:(r=!0,t=n.apply(this,arguments),n=null,t)}},w.wrap=function(n,t){return function(){var r=[n];return a.apply(r,arguments),t.apply(this,r)}},w.compose=function(){var n=arguments;return function(){for(var t=arguments,r=n.length-1;r>=0;r--)t=[n[r].apply(this,t)];return t[0]}},w.after=function(n,t){return 0>=n?t():function(){return 1>--n?t.apply(this,arguments):void 0}},w.keys=_||function(n){if(n!==Object(n))throw new TypeError("Invalid object");var t=[];for(var r in n)w.has(n,r)&&(t[t.length]=r);return t},w.values=function(n){var t=[];for(var r in n)w.has(n,r)&&t.push(n[r]);return t},w.pairs=function(n){var t=[];for(var r in n)w.has(n,r)&&t.push([r,n[r]]);return t},w.invert=function(n){var t={};for(var r in n)w.has(n,r)&&(t[n[r]]=r);return t},w.functions=w.methods=function(n){var t=[];for(var r in n)w.isFunction(n[r])&&t.push(r);return t.sort()},w.extend=function(n){return A(o.call(arguments,1),function(t){if(t)for(var r in t)n[r]=t[r]}),n},w.pick=function(n){var t={},r=c.apply(e,o.call(arguments,1));return A(r,function(r){r in n&&(t[r]=n[r])}),t},w.omit=function(n){var t={},r=c.apply(e,o.call(arguments,1));for(var u in n)w.contains(r,u)||(t[u]=n[u]);return t},w.defaults=function(n){return A(o.call(arguments,1),function(t){if(t)for(var r in t)null==n[r]&&(n[r]=t[r])}),n},w.clone=function(n){return w.isObject(n)?w.isArray(n)?n.slice():w.extend({},n):n},w.tap=function(n,t){return t(n),n};var I=function(n,t,r,e){if(n===t)return 0!==n||1/n==1/t;if(null==n||null==t)return n===t;n instanceof w&&(n=n._wrapped),t instanceof w&&(t=t._wrapped);var u=l.call(n);if(u!=l.call(t))return!1;switch(u){case"[object String]":return n==t+"";case"[object Number]":return n!=+n?t!=+t:0==n?1/n==1/t:n==+t;case"[object Date]":case"[object Boolean]":return+n==+t;case"[object RegExp]":return n.source==t.source&&n.global==t.global&&n.multiline==t.multiline&&n.ignoreCase==t.ignoreCase}if("object"!=typeof n||"object"!=typeof t)return!1;for(var i=r.length;i--;)if(r[i]==n)return e[i]==t;r.push(n),e.push(t);var a=0,o=!0;if("[object Array]"==u){if(a=n.length,o=a==t.length)for(;a--&&(o=I(n[a],t[a],r,e)););}else{var c=n.constructor,f=t.constructor;if(c!==f&&!(w.isFunction(c)&&c instanceof c&&w.isFunction(f)&&f instanceof f))return!1;for(var s in n)if(w.has(n,s)&&(a++,!(o=w.has(t,s)&&I(n[s],t[s],r,e))))break;if(o){for(s in t)if(w.has(t,s)&&!a--)break;o=!a}}return r.pop(),e.pop(),o};w.isEqual=function(n,t){return I(n,t,[],[])},w.isEmpty=function(n){if(null==n)return!0;if(w.isArray(n)||w.isString(n))return 0===n.length;for(var t in n)if(w.has(n,t))return!1;return!0},w.isElement=function(n){return!(!n||1!==n.nodeType)},w.isArray=x||function(n){return"[object Array]"==l.call(n)},w.isObject=function(n){return n===Object(n)},A(["Arguments","Function","String","Number","Date","RegExp"],function(n){w["is"+n]=function(t){return l.call(t)=="[object "+n+"]"}}),w.isArguments(arguments)||(w.isArguments=function(n){return!(!n||!w.has(n,"callee"))}),"function"!=typeof/./&&(w.isFunction=function(n){return"function"==typeof n}),w.isFinite=function(n){return isFinite(n)&&!isNaN(parseFloat(n))},w.isNaN=function(n){return w.isNumber(n)&&n!=+n},w.isBoolean=function(n){return n===!0||n===!1||"[object Boolean]"==l.call(n)},w.isNull=function(n){return null===n},w.isUndefined=function(n){return n===void 0},w.has=function(n,t){return f.call(n,t)},w.noConflict=function(){return n._=t,this},w.identity=function(n){return n},w.times=function(n,t,r){for(var e=Array(n),u=0;n>u;u++)e[u]=t.call(r,u);return e},w.random=function(n,t){return null==t&&(t=n,n=0),n+Math.floor(Math.random()*(t-n+1))};var M={escape:{"&":"&","<":"<",">":">",'"':""","'":"'","/":"/"}};M.unescape=w.invert(M.escape);var S={escape:RegExp("["+w.keys(M.escape).join("")+"]","g"),unescape:RegExp("("+w.keys(M.unescape).join("|")+")","g")};w.each(["escape","unescape"],function(n){w[n]=function(t){return null==t?"":(""+t).replace(S[n],function(t){return M[n][t]})}}),w.result=function(n,t){if(null==n)return null;var r=n[t];return w.isFunction(r)?r.call(n):r},w.mixin=function(n){A(w.functions(n),function(t){var r=w[t]=n[t];w.prototype[t]=function(){var n=[this._wrapped];return a.apply(n,arguments),D.call(this,r.apply(w,n))}})};var N=0;w.uniqueId=function(n){var t=++N+"";return n?n+t:t},w.templateSettings={evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g,escape:/<%-([\s\S]+?)%>/g};var T=/(.)^/,q={"'":"'","\\":"\\","\r":"r","\n":"n"," ":"t","\u2028":"u2028","\u2029":"u2029"},B=/\\|'|\r|\n|\t|\u2028|\u2029/g;w.template=function(n,t,r){var e;r=w.defaults({},r,w.templateSettings);var u=RegExp([(r.escape||T).source,(r.interpolate||T).source,(r.evaluate||T).source].join("|")+"|$","g"),i=0,a="__p+='";n.replace(u,function(t,r,e,u,o){return a+=n.slice(i,o).replace(B,function(n){return"\\"+q[n]}),r&&(a+="'+\n((__t=("+r+"))==null?'':_.escape(__t))+\n'"),e&&(a+="'+\n((__t=("+e+"))==null?'':__t)+\n'"),u&&(a+="';\n"+u+"\n__p+='"),i=o+t.length,t}),a+="';\n",r.variable||(a="with(obj||{}){\n"+a+"}\n"),a="var __t,__p='',__j=Array.prototype.join,"+"print=function(){__p+=__j.call(arguments,'');};\n"+a+"return __p;\n";try{e=Function(r.variable||"obj","_",a)}catch(o){throw o.source=a,o}if(t)return e(t,w);var c=function(n){return e.call(this,n,w)};return c.source="function("+(r.variable||"obj")+"){\n"+a+"}",c},w.chain=function(n){return w(n).chain()};var D=function(n){return this._chain?w(n).chain():n};w.mixin(w),A(["pop","push","reverse","shift","sort","splice","unshift"],function(n){var t=e[n];w.prototype[n]=function(){var r=this._wrapped;return t.apply(r,arguments),"shift"!=n&&"splice"!=n||0!==r.length||delete r[0],D.call(this,r)}}),A(["concat","join","slice"],function(n){var t=e[n];w.prototype[n]=function(){return D.call(this,t.apply(this._wrapped,arguments))}}),w.extend(w.prototype,{chain:function(){return this._chain=!0,this},value:function(){return this._wrapped}})}).call(this); -/* - json2.js - 2012-10-08 - - Public Domain. - - NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK. - - See http://www.JSON.org/js.html - - - This code should be minified before deployment. - See http://javascript.crockford.com/jsmin.html - - USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO - NOT CONTROL. - - - This file creates a global JSON object containing two methods: stringify - and parse. - - JSON.stringify(value, replacer, space) - value any JavaScript value, usually an object or array. - - replacer an optional parameter that determines how object - values are stringified for objects. It can be a - function or an array of strings. - - space an optional parameter that specifies the indentation - of nested structures. If it is omitted, the text will - be packed without extra whitespace. If it is a number, - it will specify the number of spaces to indent at each - level. If it is a string (such as '\t' or ' '), - it contains the characters used to indent at each level. - - This method produces a JSON text from a JavaScript value. - - When an object value is found, if the object contains a toJSON - method, its toJSON method will be called and the result will be - stringified. A toJSON method does not serialize: it returns the - value represented by the name/value pair that should be serialized, - or undefined if nothing should be serialized. The toJSON method - will be passed the key associated with the value, and this will be - bound to the value - - For example, this would serialize Dates as ISO strings. - - Date.prototype.toJSON = function (key) { - function f(n) { - // Format integers to have at least two digits. - return n < 10 ? '0' + n : n; - } - - return this.getUTCFullYear() + '-' + - f(this.getUTCMonth() + 1) + '-' + - f(this.getUTCDate()) + 'T' + - f(this.getUTCHours()) + ':' + - f(this.getUTCMinutes()) + ':' + - f(this.getUTCSeconds()) + 'Z'; - }; - - You can provide an optional replacer method. It will be passed the - key and value of each member, with this bound to the containing - object. The value that is returned from your method will be - serialized. If your method returns undefined, then the member will - be excluded from the serialization. - - If the replacer parameter is an array of strings, then it will be - used to select the members to be serialized. It filters the results - such that only members with keys listed in the replacer array are - stringified. - - Values that do not have JSON representations, such as undefined or - functions, will not be serialized. Such values in objects will be - dropped; in arrays they will be replaced with null. You can use - a replacer function to replace those with JSON values. - JSON.stringify(undefined) returns undefined. - - The optional space parameter produces a stringification of the - value that is filled with line breaks and indentation to make it - easier to read. - - If the space parameter is a non-empty string, then that string will - be used for indentation. If the space parameter is a number, then - the indentation will be that many spaces. - - Example: - - text = JSON.stringify(['e', {pluribus: 'unum'}]); - // text is '["e",{"pluribus":"unum"}]' - - - text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t'); - // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]' - - text = JSON.stringify([new Date()], function (key, value) { - return this[key] instanceof Date ? - 'Date(' + this[key] + ')' : value; - }); - // text is '["Date(---current time---)"]' - - - JSON.parse(text, reviver) - This method parses a JSON text to produce an object or array. - It can throw a SyntaxError exception. - - The optional reviver parameter is a function that can filter and - transform the results. It receives each of the keys and values, - and its return value is used instead of the original value. - If it returns what it received, then the structure is not modified. - If it returns undefined then the member is deleted. - - Example: - - // Parse the text. Values that look like ISO date strings will - // be converted to Date objects. - - myData = JSON.parse(text, function (key, value) { - var a; - if (typeof value === 'string') { - a = -/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value); - if (a) { - return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4], - +a[5], +a[6])); - } - } - return value; - }); - - myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) { - var d; - if (typeof value === 'string' && - value.slice(0, 5) === 'Date(' && - value.slice(-1) === ')') { - d = new Date(value.slice(5, -1)); - if (d) { - return d; - } - } - return value; - }); - - - This is a reference implementation. You are free to copy, modify, or - redistribute. -*/ - -/*jslint evil: true, regexp: true */ - -/*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", apply, - call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours, - getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join, - lastIndex, length, parse, prototype, push, replace, slice, stringify, - test, toJSON, toString, valueOf -*/ - - -// Create a JSON object only if one does not already exist. We create the -// methods in a closure to avoid creating global variables. - -if (typeof JSON !== 'object') { - JSON = {}; -} - -(function () { - 'use strict'; - - function f(n) { - // Format integers to have at least two digits. - return n < 10 ? '0' + n : n; - } - - if (typeof Date.prototype.toJSON !== 'function') { - - Date.prototype.toJSON = function (key) { - - return isFinite(this.valueOf()) - ? this.getUTCFullYear() + '-' + - f(this.getUTCMonth() + 1) + '-' + - f(this.getUTCDate()) + 'T' + - f(this.getUTCHours()) + ':' + - f(this.getUTCMinutes()) + ':' + - f(this.getUTCSeconds()) + 'Z' - : null; - }; - - String.prototype.toJSON = - Number.prototype.toJSON = - Boolean.prototype.toJSON = function (key) { - return this.valueOf(); - }; - } - - var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, - escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, - gap, - indent, - meta = { // table of character substitutions - '\b': '\\b', - '\t': '\\t', - '\n': '\\n', - '\f': '\\f', - '\r': '\\r', - '"' : '\\"', - '\\': '\\\\' - }, - rep; - - - function quote(string) { - -// If the string contains no control characters, no quote characters, and no -// backslash characters, then we can safely slap some quotes around it. -// Otherwise we must also replace the offending characters with safe escape -// sequences. - - escapable.lastIndex = 0; - return escapable.test(string) ? '"' + string.replace(escapable, function (a) { - var c = meta[a]; - return typeof c === 'string' - ? c - : '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4); - }) + '"' : '"' + string + '"'; - } - - - function str(key, holder) { - -// Produce a string from holder[key]. - - var i, // The loop counter. - k, // The member key. - v, // The member value. - length, - mind = gap, - partial, - value = holder[key]; - -// If the value has a toJSON method, call it to obtain a replacement value. - - if (value && typeof value === 'object' && - typeof value.toJSON === 'function') { - value = value.toJSON(key); - } - -// If we were called with a replacer function, then call the replacer to -// obtain a replacement value. - - if (typeof rep === 'function') { - value = rep.call(holder, key, value); - } - -// What happens next depends on the value's type. - - switch (typeof value) { - case 'string': - return quote(value); - - case 'number': - -// JSON numbers must be finite. Encode non-finite numbers as null. - - return isFinite(value) ? String(value) : 'null'; - - case 'boolean': - case 'null': - -// If the value is a boolean or null, convert it to a string. Note: -// typeof null does not produce 'null'. The case is included here in -// the remote chance that this gets fixed someday. - - return String(value); - -// If the type is 'object', we might be dealing with an object or an array or -// null. - - case 'object': - -// Due to a specification blunder in ECMAScript, typeof null is 'object', -// so watch out for that case. - - if (!value) { - return 'null'; - } - -// Make an array to hold the partial results of stringifying this object value. - - gap += indent; - partial = []; - -// Is the value an array? - - if (Object.prototype.toString.apply(value) === '[object Array]') { - -// The value is an array. Stringify every element. Use null as a placeholder -// for non-JSON values. - - length = value.length; - for (i = 0; i < length; i += 1) { - partial[i] = str(i, value) || 'null'; - } - -// Join all of the elements together, separated with commas, and wrap them in -// brackets. - - v = partial.length === 0 - ? '[]' - : gap - ? '[\n' + gap + partial.join(',\n' + gap) + '\n' + mind + ']' - : '[' + partial.join(',') + ']'; - gap = mind; - return v; - } - -// If the replacer is an array, use it to select the members to be stringified. - - if (rep && typeof rep === 'object') { - length = rep.length; - for (i = 0; i < length; i += 1) { - if (typeof rep[i] === 'string') { - k = rep[i]; - v = str(k, value); - if (v) { - partial.push(quote(k) + (gap ? ': ' : ':') + v); - } - } - } - } else { - -// Otherwise, iterate through all of the keys in the object. - - for (k in value) { - if (Object.prototype.hasOwnProperty.call(value, k)) { - v = str(k, value); - if (v) { - partial.push(quote(k) + (gap ? ': ' : ':') + v); - } - } - } - } - -// Join all of the member texts together, separated with commas, -// and wrap them in braces. - - v = partial.length === 0 - ? '{}' - : gap - ? '{\n' + gap + partial.join(',\n' + gap) + '\n' + mind + '}' - : '{' + partial.join(',') + '}'; - gap = mind; - return v; - } - } - -// If the JSON object does not yet have a stringify method, give it one. - - if (typeof JSON.stringify !== 'function') { - JSON.stringify = function (value, replacer, space) { - -// The stringify method takes a value and an optional replacer, and an optional -// space parameter, and returns a JSON text. The replacer can be a function -// that can replace values, or an array of strings that will select the keys. -// A default replacer method can be provided. Use of the space parameter can -// produce text that is more easily readable. - - var i; - gap = ''; - indent = ''; - -// If the space parameter is a number, make an indent string containing that -// many spaces. - - if (typeof space === 'number') { - for (i = 0; i < space; i += 1) { - indent += ' '; - } - -// If the space parameter is a string, it will be used as the indent string. - - } else if (typeof space === 'string') { - indent = space; - } - -// If there is a replacer, it must be a function or an array. -// Otherwise, throw an error. - - rep = replacer; - if (replacer && typeof replacer !== 'function' && - (typeof replacer !== 'object' || - typeof replacer.length !== 'number')) { - throw new Error('JSON.stringify'); - } - -// Make a fake root object containing our value under the key of ''. -// Return the result of stringifying the value. - - return str('', {'': value}); - }; - } - - -// If the JSON object does not yet have a parse method, give it one. - - if (typeof JSON.parse !== 'function') { - JSON.parse = function (text, reviver) { - -// The parse method takes a text and an optional reviver function, and returns -// a JavaScript value if the text is a valid JSON text. - - var j; - - function walk(holder, key) { - -// The walk method is used to recursively walk the resulting structure so -// that modifications can be made. - - var k, v, value = holder[key]; - if (value && typeof value === 'object') { - for (k in value) { - if (Object.prototype.hasOwnProperty.call(value, k)) { - v = walk(value, k); - if (v !== undefined) { - value[k] = v; - } else { - delete value[k]; - } - } - } - } - return reviver.call(holder, key, value); - } - - -// Parsing happens in four stages. In the first stage, we replace certain -// Unicode characters with escape sequences. JavaScript handles many characters -// incorrectly, either silently deleting them, or treating them as line endings. - - text = String(text); - cx.lastIndex = 0; - if (cx.test(text)) { - text = text.replace(cx, function (a) { - return '\\u' + - ('0000' + a.charCodeAt(0).toString(16)).slice(-4); - }); - } - -// In the second stage, we run the text against regular expressions that look -// for non-JSON patterns. We are especially concerned with '()' and 'new' -// because they can cause invocation, and '=' because it can cause mutation. -// But just to be safe, we want to reject all unexpected forms. - -// We split the second stage into 4 regexp operations in order to work around -// crippling inefficiencies in IE's and Safari's regexp engines. First we -// replace the JSON backslash pairs with '@' (a non-JSON character). Second, we -// replace all simple value tokens with ']' characters. Third, we delete all -// open brackets that follow a colon or comma or that begin the text. Finally, -// we look to see that the remaining characters are only whitespace or ']' or -// ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval. - - if (/^[\],:{}\s]*$/ - .test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@') - .replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']') - .replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) { - -// In the third stage we use the eval function to compile the text into a -// JavaScript structure. The '{' operator is subject to a syntactic ambiguity -// in JavaScript: it can begin a block or an object literal. We wrap the text -// in parens to eliminate the ambiguity. - - j = eval('(' + text + ')'); - -// In the optional fourth stage, we recursively walk the new structure, passing -// each name/value pair to a reviver function for possible transformation. - - return typeof reviver === 'function' - ? walk({'': j}, '') - : j; - } - -// If the text is not JSON parseable, then a SyntaxError is thrown. - - throw new SyntaxError('JSON.parse'); - }; - } -}()); -// Backbone.js 0.9.2 - -// (c) 2010-2012 Jeremy Ashkenas, DocumentCloud Inc. -// Backbone may be freely distributed under the MIT license. -// For all details and documentation: -// http://backbonejs.org - -(function(){ - - // Initial Setup - // ------------- - - // Save a reference to the global object (`window` in the browser, `global` - // on the server). - var root = this; - - // Save the previous value of the `Backbone` variable, so that it can be - // restored later on, if `noConflict` is used. - var previousBackbone = root.Backbone; - - // Create a local reference to slice/splice. - var slice = Array.prototype.slice; - var splice = Array.prototype.splice; - - // The top-level namespace. All public Backbone classes and modules will - // be attached to this. Exported for both CommonJS and the browser. - var Backbone; - if (typeof exports !== 'undefined') { - Backbone = exports; - } else { - Backbone = root.Backbone = {}; - } - - // Current version of the library. Keep in sync with `package.json`. - Backbone.VERSION = '0.9.2'; - - // Require Underscore, if we're on the server, and it's not already present. - var _ = root._; - if (!_ && (typeof require !== 'undefined')) _ = require('underscore'); - - // For Backbone's purposes, jQuery, Zepto, or Ender owns the `$` variable. - var $ = root.jQuery || root.Zepto || root.ender; - - // Set the JavaScript library that will be used for DOM manipulation and - // Ajax calls (a.k.a. the `$` variable). By default Backbone will use: jQuery, - // Zepto, or Ender; but the `setDomLibrary()` method lets you inject an - // alternate JavaScript library (or a mock library for testing your views - // outside of a browser). - Backbone.setDomLibrary = function(lib) { - $ = lib; - }; - - // Runs Backbone.js in *noConflict* mode, returning the `Backbone` variable - // to its previous owner. Returns a reference to this Backbone object. - Backbone.noConflict = function() { - root.Backbone = previousBackbone; - return this; - }; - - // Turn on `emulateHTTP` to support legacy HTTP servers. Setting this option - // will fake `"PUT"` and `"DELETE"` requests via the `_method` parameter and - // set a `X-Http-Method-Override` header. - Backbone.emulateHTTP = false; - - // Turn on `emulateJSON` to support legacy servers that can't deal with direct - // `application/json` requests ... will encode the body as - // `application/x-www-form-urlencoded` instead and will send the model in a - // form param named `model`. - Backbone.emulateJSON = false; - - // Backbone.Events - // ----------------- - - // Regular expression used to split event strings - var eventSplitter = /\s+/; - - // A module that can be mixed in to *any object* in order to provide it with - // custom events. You may bind with `on` or remove with `off` callback functions - // to an event; trigger`-ing an event fires all callbacks in succession. - // - // var object = {}; - // _.extend(object, Backbone.Events); - // object.on('expand', function(){ alert('expanded'); }); - // object.trigger('expand'); - // - var Events = Backbone.Events = { - - // Bind one or more space separated events, `events`, to a `callback` - // function. Passing `"all"` will bind the callback to all events fired. - on: function(events, callback, context) { - - var calls, event, node, tail, list; - if (!callback) return this; - events = events.split(eventSplitter); - calls = this._callbacks || (this._callbacks = {}); - - // Create an immutable callback list, allowing traversal during - // modification. The tail is an empty object that will always be used - // as the next node. - while (event = events.shift()) { - list = calls[event]; - node = list ? list.tail : {}; - node.next = tail = {}; - node.context = context; - node.callback = callback; - calls[event] = {tail: tail, next: list ? list.next : node}; - } - - return this; - }, - - // Remove one or many callbacks. If `context` is null, removes all callbacks - // with that function. If `callback` is null, removes all callbacks for the - // event. If `events` is null, removes all bound callbacks for all events. - off: function(events, callback, context) { - var event, calls, node, tail, cb, ctx; - - // No events, or removing *all* events. - if (!(calls = this._callbacks)) return; - if (!(events || callback || context)) { - delete this._callbacks; - return this; - } - - // Loop through the listed events and contexts, splicing them out of the - // linked list of callbacks if appropriate. - events = events ? events.split(eventSplitter) : _.keys(calls); - while (event = events.shift()) { - node = calls[event]; - delete calls[event]; - if (!node || !(callback || context)) continue; - // Create a new list, omitting the indicated callbacks. - tail = node.tail; - while ((node = node.next) !== tail) { - cb = node.callback; - ctx = node.context; - if ((callback && cb !== callback) || (context && ctx !== context)) { - this.on(event, cb, ctx); - } - } - } - - return this; - }, - - // Trigger one or many events, firing all bound callbacks. Callbacks are - // passed the same arguments as `trigger` is, apart from the event name - // (unless you're listening on `"all"`, which will cause your callback to - // receive the true name of the event as the first argument). - trigger: function(events) { - var event, node, calls, tail, args, all, rest; - if (!(calls = this._callbacks)) return this; - all = calls.all; - events = events.split(eventSplitter); - rest = slice.call(arguments, 1); - - // For each event, walk through the linked list of callbacks twice, - // first to trigger the event, then to trigger any `"all"` callbacks. - while (event = events.shift()) { - if (node = calls[event]) { - tail = node.tail; - while ((node = node.next) !== tail) { - node.callback.apply(node.context || this, rest); - } - } - if (node = all) { - tail = node.tail; - args = [event].concat(rest); - while ((node = node.next) !== tail) { - node.callback.apply(node.context || this, args); - } - } - } - - return this; - } - - }; - - // Aliases for backwards compatibility. - Events.bind = Events.on; - Events.unbind = Events.off; - - // Backbone.Model - // -------------- - - // Create a new model, with defined attributes. A client id (`cid`) - // is automatically generated and assigned for you. - var Model = Backbone.Model = function(attributes, options) { - var defaults; - attributes || (attributes = {}); - if (options && options.parse) attributes = this.parse(attributes); - if (defaults = getValue(this, 'defaults')) { - attributes = _.extend({}, defaults, attributes); - } - if (options && options.collection) this.collection = options.collection; - this.attributes = {}; - this._escapedAttributes = {}; - this.cid = _.uniqueId('c'); - this.changed = {}; - this._silent = {}; - this._pending = {}; - this.set(attributes, {silent: true}); - // Reset change tracking. - this.changed = {}; - this._silent = {}; - this._pending = {}; - this._previousAttributes = _.clone(this.attributes); - this.initialize.apply(this, arguments); - }; - - // Attach all inheritable methods to the Model prototype. - _.extend(Model.prototype, Events, { - - // A hash of attributes whose current and previous value differ. - changed: null, - - // A hash of attributes that have silently changed since the last time - // `change` was called. Will become pending attributes on the next call. - _silent: null, - - // A hash of attributes that have changed since the last `'change'` event - // began. - _pending: null, - - // The default name for the JSON `id` attribute is `"id"`. MongoDB and - // CouchDB users may want to set this to `"_id"`. - idAttribute: 'id', - - // Initialize is an empty function by default. Override it with your own - // initialization logic. - initialize: function(){}, - - // Return a copy of the model's `attributes` object. - toJSON: function(options) { - return _.clone(this.attributes); - }, - - // Get the value of an attribute. - get: function(attr) { - return this.attributes[attr]; - }, - - // Get the HTML-escaped value of an attribute. - escape: function(attr) { - var html; - if (html = this._escapedAttributes[attr]) return html; - var val = this.get(attr); - return this._escapedAttributes[attr] = _.escape(val == null ? '' : '' + val); - }, - - // Returns `true` if the attribute contains a value that is not null - // or undefined. - has: function(attr) { - return this.get(attr) != null; - }, - - // Set a hash of model attributes on the object, firing `"change"` unless - // you choose to silence it. - set: function(key, value, options) { - var attrs, attr, val; - - // Handle both `"key", value` and `{key: value}` -style arguments. - if (_.isObject(key) || key == null) { - attrs = key; - options = value; - } else { - attrs = {}; - attrs[key] = value; - } - - // Extract attributes and options. - options || (options = {}); - if (!attrs) return this; - if (attrs instanceof Model) attrs = attrs.attributes; - if (options.unset) for (attr in attrs) attrs[attr] = void 0; - - // Run validation. - if (!this._validate(attrs, options)) return false; - - // Check for changes of `id`. - if (this.idAttribute in attrs) this.id = attrs[this.idAttribute]; - - var changes = options.changes = {}; - var now = this.attributes; - var escaped = this._escapedAttributes; - var prev = this._previousAttributes || {}; - - // For each `set` attribute... - for (attr in attrs) { - val = attrs[attr]; - - // If the new and current value differ, record the change. - if (!_.isEqual(now[attr], val) || (options.unset && _.has(now, attr))) { - delete escaped[attr]; - (options.silent ? this._silent : changes)[attr] = true; - } - - // Update or delete the current value. - options.unset ? delete now[attr] : now[attr] = val; - - // If the new and previous value differ, record the change. If not, - // then remove changes for this attribute. - if (!_.isEqual(prev[attr], val) || (_.has(now, attr) != _.has(prev, attr))) { - this.changed[attr] = val; - if (!options.silent) this._pending[attr] = true; - } else { - delete this.changed[attr]; - delete this._pending[attr]; - } - } - - // Fire the `"change"` events. - if (!options.silent) this.change(options); - return this; - }, - - // Remove an attribute from the model, firing `"change"` unless you choose - // to silence it. `unset` is a noop if the attribute doesn't exist. - unset: function(attr, options) { - (options || (options = {})).unset = true; - return this.set(attr, null, options); - }, - - // Clear all attributes on the model, firing `"change"` unless you choose - // to silence it. - clear: function(options) { - (options || (options = {})).unset = true; - return this.set(_.clone(this.attributes), options); - }, - - // Fetch the model from the server. If the server's representation of the - // model differs from its current attributes, they will be overriden, - // triggering a `"change"` event. - fetch: function(options) { - options = options ? _.clone(options) : {}; - var model = this; - var success = options.success; - options.success = function(resp, status, xhr) { - if (!model.set(model.parse(resp, xhr), options)) return false; - if (success) success(model, resp); - }; - options.error = Backbone.wrapError(options.error, model, options); - return (this.sync || Backbone.sync).call(this, 'read', this, options); - }, - - // Set a hash of model attributes, and sync the model to the server. - // If the server returns an attributes hash that differs, the model's - // state will be `set` again. - save: function(key, value, options) { - var attrs, current; - - // Handle both `("key", value)` and `({key: value})` -style calls. - if (_.isObject(key) || key == null) { - attrs = key; - options = value; - } else { - attrs = {}; - attrs[key] = value; - } - options = options ? _.clone(options) : {}; - - // If we're "wait"-ing to set changed attributes, validate early. - if (options.wait) { - if (!this._validate(attrs, options)) return false; - current = _.clone(this.attributes); - } - - // Regular saves `set` attributes before persisting to the server. - var silentOptions = _.extend({}, options, {silent: true}); - if (attrs && !this.set(attrs, options.wait ? silentOptions : options)) { - return false; - } - - // After a successful server-side save, the client is (optionally) - // updated with the server-side state. - var model = this; - var success = options.success; - options.success = function(resp, status, xhr) { - var serverAttrs = model.parse(resp, xhr); - if (options.wait) { - delete options.wait; - serverAttrs = _.extend(attrs || {}, serverAttrs); - } - if (!model.set(serverAttrs, options)) return false; - if (success) { - success(model, resp); - } else { - model.trigger('sync', model, resp, options); - } - }; - - // Finish configuring and sending the Ajax request. - options.error = Backbone.wrapError(options.error, model, options); - var method = this.isNew() ? 'create' : 'update'; - var xhr = (this.sync || Backbone.sync).call(this, method, this, options); - if (options.wait) this.set(current, silentOptions); - return xhr; - }, - - // Destroy this model on the server if it was already persisted. - // Optimistically removes the model from its collection, if it has one. - // If `wait: true` is passed, waits for the server to respond before removal. - destroy: function(options) { - options = options ? _.clone(options) : {}; - var model = this; - var success = options.success; - - var triggerDestroy = function() { - model.trigger('destroy', model, model.collection, options); - }; - - if (this.isNew()) { - triggerDestroy(); - return false; - } - - options.success = function(resp) { - if (options.wait) triggerDestroy(); - if (success) { - success(model, resp); - } else { - model.trigger('sync', model, resp, options); - } - }; - - options.error = Backbone.wrapError(options.error, model, options); - var xhr = (this.sync || Backbone.sync).call(this, 'delete', this, options); - if (!options.wait) triggerDestroy(); - return xhr; - }, - - // Default URL for the model's representation on the server -- if you're - // using Backbone's restful methods, override this to change the endpoint - // that will be called. - url: function() { - var base = getValue(this, 'urlRoot') || getValue(this.collection, 'url') || urlError(); - if (this.isNew()) return base; - return base + (base.charAt(base.length - 1) == '/' ? '' : '/') + encodeURIComponent(this.id); - }, - - // **parse** converts a response into the hash of attributes to be `set` on - // the model. The default implementation is just to pass the response along. - parse: function(resp, xhr) { - return resp; - }, - - // Create a new model with identical attributes to this one. - clone: function() { - return new this.constructor(this.attributes); - }, - - // A model is new if it has never been saved to the server, and lacks an id. - isNew: function() { - return this.id == null; - }, - - // Call this method to manually fire a `"change"` event for this model and - // a `"change:attribute"` event for each changed attribute. - // Calling this will cause all objects observing the model to update. - change: function(options) { - options || (options = {}); - var changing = this._changing; - this._changing = true; - - // Silent changes become pending changes. - for (var attr in this._silent) this._pending[attr] = true; - - // Silent changes are triggered. - var changes = _.extend({}, options.changes, this._silent); - this._silent = {}; - for (var attr in changes) { - this.trigger('change:' + attr, this, this.get(attr), options); - } - if (changing) return this; - - // Continue firing `"change"` events while there are pending changes. - while (!_.isEmpty(this._pending)) { - this._pending = {}; - this.trigger('change', this, options); - // Pending and silent changes still remain. - for (var attr in this.changed) { - if (this._pending[attr] || this._silent[attr]) continue; - delete this.changed[attr]; - } - this._previousAttributes = _.clone(this.attributes); - } - - this._changing = false; - return this; - }, - - // Determine if the model has changed since the last `"change"` event. - // If you specify an attribute name, determine if that attribute has changed. - hasChanged: function(attr) { - if (!arguments.length) return !_.isEmpty(this.changed); - return _.has(this.changed, attr); - }, - - // Return an object containing all the attributes that have changed, or - // false if there are no changed attributes. Useful for determining what - // parts of a view need to be updated and/or what attributes need to be - // persisted to the server. Unset attributes will be set to undefined. - // You can also pass an attributes object to diff against the model, - // determining if there *would be* a change. - changedAttributes: function(diff) { - if (!diff) return this.hasChanged() ? _.clone(this.changed) : false; - var val, changed = false, old = this._previousAttributes; - for (var attr in diff) { - if (_.isEqual(old[attr], (val = diff[attr]))) continue; - (changed || (changed = {}))[attr] = val; - } - return changed; - }, - - // Get the previous value of an attribute, recorded at the time the last - // `"change"` event was fired. - previous: function(attr) { - if (!arguments.length || !this._previousAttributes) return null; - return this._previousAttributes[attr]; - }, - - // Get all of the attributes of the model at the time of the previous - // `"change"` event. - previousAttributes: function() { - return _.clone(this._previousAttributes); - }, - - // Check if the model is currently in a valid state. It's only possible to - // get into an *invalid* state if you're using silent changes. - isValid: function() { - return !this.validate(this.attributes); - }, - - // Run validation against the next complete set of model attributes, - // returning `true` if all is well. If a specific `error` callback has - // been passed, call that instead of firing the general `"error"` event. - _validate: function(attrs, options) { - if (options.silent || !this.validate) return true; - attrs = _.extend({}, this.attributes, attrs); - var error = this.validate(attrs, options); - if (!error) return true; - if (options && options.error) { - options.error(this, error, options); - } else { - this.trigger('error', this, error, options); - } - return false; - } - - }); - - // Backbone.Collection - // ------------------- - - // Provides a standard collection class for our sets of models, ordered - // or unordered. If a `comparator` is specified, the Collection will maintain - // its models in sort order, as they're added and removed. - var Collection = Backbone.Collection = function(models, options) { - options || (options = {}); - if (options.model) this.model = options.model; - if (options.comparator) this.comparator = options.comparator; - this._reset(); - this.initialize.apply(this, arguments); - if (models) this.reset(models, {silent: true, parse: options.parse}); - }; - - // Define the Collection's inheritable methods. - _.extend(Collection.prototype, Events, { - - // The default model for a collection is just a **Backbone.Model**. - // This should be overridden in most cases. - model: Model, - - // Initialize is an empty function by default. Override it with your own - // initialization logic. - initialize: function(){}, - - // The JSON representation of a Collection is an array of the - // models' attributes. - toJSON: function(options) { - return this.map(function(model){ return model.toJSON(options); }); - }, - - // Add a model, or list of models to the set. Pass **silent** to avoid - // firing the `add` event for every new model. - add: function(models, options) { - var i, index, length, model, cid, id, cids = {}, ids = {}, dups = []; - options || (options = {}); - models = _.isArray(models) ? models.slice() : [models]; - - // Begin by turning bare objects into model references, and preventing - // invalid models or duplicate models from being added. - for (i = 0, length = models.length; i < length; i++) { - if (!(model = models[i] = this._prepareModel(models[i], options))) { - throw new Error("Can't add an invalid model to a collection"); - } - cid = model.cid; - id = model.id; - if (cids[cid] || this._byCid[cid] || ((id != null) && (ids[id] || this._byId[id]))) { - dups.push(i); - continue; - } - cids[cid] = ids[id] = model; - } - - // Remove duplicates. - i = dups.length; - while (i--) { - models.splice(dups[i], 1); - } - - // Listen to added models' events, and index models for lookup by - // `id` and by `cid`. - for (i = 0, length = models.length; i < length; i++) { - (model = models[i]).on('all', this._onModelEvent, this); - this._byCid[model.cid] = model; - if (model.id != null) this._byId[model.id] = model; - } - - // Insert models into the collection, re-sorting if needed, and triggering - // `add` events unless silenced. - this.length += length; - index = options.at != null ? options.at : this.models.length; - splice.apply(this.models, [index, 0].concat(models)); - if (this.comparator) this.sort({silent: true}); - if (options.silent) return this; - for (i = 0, length = this.models.length; i < length; i++) { - if (!cids[(model = this.models[i]).cid]) continue; - options.index = i; - model.trigger('add', model, this, options); - } - return this; - }, - - // Remove a model, or a list of models from the set. Pass silent to avoid - // firing the `remove` event for every model removed. - remove: function(models, options) { - var i, l, index, model; - options || (options = {}); - models = _.isArray(models) ? models.slice() : [models]; - for (i = 0, l = models.length; i < l; i++) { - model = this.getByCid(models[i]) || this.get(models[i]); - if (!model) continue; - delete this._byId[model.id]; - delete this._byCid[model.cid]; - index = this.indexOf(model); - this.models.splice(index, 1); - this.length--; - if (!options.silent) { - options.index = index; - model.trigger('remove', model, this, options); - } - this._removeReference(model); - } - return this; - }, - - // Add a model to the end of the collection. - push: function(model, options) { - model = this._prepareModel(model, options); - this.add(model, options); - return model; - }, - - // Remove a model from the end of the collection. - pop: function(options) { - var model = this.at(this.length - 1); - this.remove(model, options); - return model; - }, - - // Add a model to the beginning of the collection. - unshift: function(model, options) { - model = this._prepareModel(model, options); - this.add(model, _.extend({at: 0}, options)); - return model; - }, - - // Remove a model from the beginning of the collection. - shift: function(options) { - var model = this.at(0); - this.remove(model, options); - return model; - }, - - // Get a model from the set by id. - get: function(id) { - if (id == null) return void 0; - return this._byId[id.id != null ? id.id : id]; - }, - - // Get a model from the set by client id. - getByCid: function(cid) { - return cid && this._byCid[cid.cid || cid]; - }, - - // Get the model at the given index. - at: function(index) { - return this.models[index]; - }, - - // Return models with matching attributes. Useful for simple cases of `filter`. - where: function(attrs) { - if (_.isEmpty(attrs)) return []; - return this.filter(function(model) { - for (var key in attrs) { - if (attrs[key] !== model.get(key)) return false; - } - return true; - }); - }, - - // Force the collection to re-sort itself. You don't need to call this under - // normal circumstances, as the set will maintain sort order as each item - // is added. - sort: function(options) { - options || (options = {}); - if (!this.comparator) throw new Error('Cannot sort a set without a comparator'); - var boundComparator = _.bind(this.comparator, this); - if (this.comparator.length == 1) { - this.models = this.sortBy(boundComparator); - } else { - this.models.sort(boundComparator); - } - if (!options.silent) this.trigger('reset', this, options); - return this; - }, - - // Pluck an attribute from each model in the collection. - pluck: function(attr) { - return _.map(this.models, function(model){ return model.get(attr); }); - }, - - // When you have more items than you want to add or remove individually, - // you can reset the entire set with a new list of models, without firing - // any `add` or `remove` events. Fires `reset` when finished. - reset: function(models, options) { - models || (models = []); - options || (options = {}); - for (var i = 0, l = this.models.length; i < l; i++) { - this._removeReference(this.models[i]); - } - this._reset(); - this.add(models, _.extend({silent: true}, options)); - if (!options.silent) this.trigger('reset', this, options); - return this; - }, - - // Fetch the default set of models for this collection, resetting the - // collection when they arrive. If `add: true` is passed, appends the - // models to the collection instead of resetting. - fetch: function(options) { - options = options ? _.clone(options) : {}; - if (options.parse === undefined) options.parse = true; - var collection = this; - var success = options.success; - options.success = function(resp, status, xhr) { - collection[options.add ? 'add' : 'reset'](collection.parse(resp, xhr), options); - if (success) success(collection, resp); - }; - options.error = Backbone.wrapError(options.error, collection, options); - return (this.sync || Backbone.sync).call(this, 'read', this, options); - }, - - // Create a new instance of a model in this collection. Add the model to the - // collection immediately, unless `wait: true` is passed, in which case we - // wait for the server to agree. - create: function(model, options) { - var coll = this; - options = options ? _.clone(options) : {}; - model = this._prepareModel(model, options); - if (!model) return false; - if (!options.wait) coll.add(model, options); - var success = options.success; - options.success = function(nextModel, resp, xhr) { - if (options.wait) coll.add(nextModel, options); - if (success) { - success(nextModel, resp); - } else { - nextModel.trigger('sync', model, resp, options); - } - }; - model.save(null, options); - return model; - }, - - // **parse** converts a response into a list of models to be added to the - // collection. The default implementation is just to pass it through. - parse: function(resp, xhr) { - return resp; - }, - - // Proxy to _'s chain. Can't be proxied the same way the rest of the - // underscore methods are proxied because it relies on the underscore - // constructor. - chain: function () { - return _(this.models).chain(); - }, - - // Reset all internal state. Called when the collection is reset. - _reset: function(options) { - this.length = 0; - this.models = []; - this._byId = {}; - this._byCid = {}; - }, - - // Prepare a model or hash of attributes to be added to this collection. - _prepareModel: function(model, options) { - options || (options = {}); - if (!(model instanceof Model)) { - var attrs = model; - options.collection = this; - model = new this.model(attrs, options); - if (!model._validate(model.attributes, options)) model = false; - } else if (!model.collection) { - model.collection = this; - } - return model; - }, - - // Internal method to remove a model's ties to a collection. - _removeReference: function(model) { - if (this == model.collection) { - delete model.collection; - } - model.off('all', this._onModelEvent, this); - }, - - // Internal method called every time a model in the set fires an event. - // Sets need to update their indexes when models change ids. All other - // events simply proxy through. "add" and "remove" events that originate - // in other collections are ignored. - _onModelEvent: function(event, model, collection, options) { - if ((event == 'add' || event == 'remove') && collection != this) return; - if (event == 'destroy') { - this.remove(model, options); - } - if (model && event === 'change:' + model.idAttribute) { - delete this._byId[model.previous(model.idAttribute)]; - this._byId[model.id] = model; - } - this.trigger.apply(this, arguments); - } - - }); - - // Underscore methods that we want to implement on the Collection. - var methods = ['forEach', 'each', 'map', 'reduce', 'reduceRight', 'find', - 'detect', 'filter', 'select', 'reject', 'every', 'all', 'some', 'any', - 'include', 'contains', 'invoke', 'max', 'min', 'sortBy', 'sortedIndex', - 'toArray', 'size', 'first', 'initial', 'rest', 'last', 'without', 'indexOf', - 'shuffle', 'lastIndexOf', 'isEmpty', 'groupBy']; - - // Mix in each Underscore method as a proxy to `Collection#models`. - _.each(methods, function(method) { - Collection.prototype[method] = function() { - return _[method].apply(_, [this.models].concat(_.toArray(arguments))); - }; - }); - - // Backbone.Router - // ------------------- - - // Routers map faux-URLs to actions, and fire events when routes are - // matched. Creating a new one sets its `routes` hash, if not set statically. - var Router = Backbone.Router = function(options) { - options || (options = {}); - if (options.routes) this.routes = options.routes; - this._bindRoutes(); - this.initialize.apply(this, arguments); - }; - - // Cached regular expressions for matching named param parts and splatted - // parts of route strings. - var namedParam = /:\w+/g; - var splatParam = /\*\w+/g; - var escapeRegExp = /[-[\]{}()+?.,\\^$|#\s]/g; - - // Set up all inheritable **Backbone.Router** properties and methods. - _.extend(Router.prototype, Events, { - - // Initialize is an empty function by default. Override it with your own - // initialization logic. - initialize: function(){}, - - // Manually bind a single named route to a callback. For example: - // - // this.route('search/:query/p:num', 'search', function(query, num) { - // ... - // }); - // - route: function(route, name, callback) { - Backbone.history || (Backbone.history = new History); - if (!_.isRegExp(route)) route = this._routeToRegExp(route); - if (!callback) callback = this[name]; - Backbone.history.route(route, _.bind(function(fragment) { - var args = this._extractParameters(route, fragment); - callback && callback.apply(this, args); - this.trigger.apply(this, ['route:' + name].concat(args)); - Backbone.history.trigger('route', this, name, args); - }, this)); - return this; - }, - - // Simple proxy to `Backbone.history` to save a fragment into the history. - navigate: function(fragment, options) { - Backbone.history.navigate(fragment, options); - }, - - // Bind all defined routes to `Backbone.history`. We have to reverse the - // order of the routes here to support behavior where the most general - // routes can be defined at the bottom of the route map. - _bindRoutes: function() { - if (!this.routes) return; - var routes = []; - for (var route in this.routes) { - routes.unshift([route, this.routes[route]]); - } - for (var i = 0, l = routes.length; i < l; i++) { - this.route(routes[i][0], routes[i][1], this[routes[i][1]]); - } - }, - - // Convert a route string into a regular expression, suitable for matching - // against the current location hash. - _routeToRegExp: function(route) { - route = route.replace(escapeRegExp, '\\$&') - .replace(namedParam, '([^\/]+)') - .replace(splatParam, '(.*?)'); - return new RegExp('^' + route + '$'); - }, - - // Given a route, and a URL fragment that it matches, return the array of - // extracted parameters. - _extractParameters: function(route, fragment) { - return route.exec(fragment).slice(1); - } - - }); - - // Backbone.History - // ---------------- - - // Handles cross-browser history management, based on URL fragments. If the - // browser does not support `onhashchange`, falls back to polling. - var History = Backbone.History = function() { - this.handlers = []; - _.bindAll(this, 'checkUrl'); - }; - - // Cached regex for cleaning leading hashes and slashes . - var routeStripper = /^[#\/]/; - - // Cached regex for detecting MSIE. - var isExplorer = /msie [\w.]+/; - - // Has the history handling already been started? - History.started = false; - - // Set up all inheritable **Backbone.History** properties and methods. - _.extend(History.prototype, Events, { - - // The default interval to poll for hash changes, if necessary, is - // twenty times a second. - interval: 50, - - // Gets the true hash value. Cannot use location.hash directly due to bug - // in Firefox where location.hash will always be decoded. - getHash: function(windowOverride) { - var loc = windowOverride ? windowOverride.location : window.location; - var match = loc.href.match(/#(.*)$/); - return match ? match[1] : ''; - }, - - // Get the cross-browser normalized URL fragment, either from the URL, - // the hash, or the override. - getFragment: function(fragment, forcePushState) { - if (fragment == null) { - if (this._hasPushState || forcePushState) { - fragment = window.location.pathname; - var search = window.location.search; - if (search) fragment += search; - } else { - fragment = this.getHash(); - } - } - if (!fragment.indexOf(this.options.root)) fragment = fragment.substr(this.options.root.length); - return fragment.replace(routeStripper, ''); - }, - - // Start the hash change handling, returning `true` if the current URL matches - // an existing route, and `false` otherwise. - start: function(options) { - if (History.started) throw new Error("Backbone.history has already been started"); - History.started = true; - - // Figure out the initial configuration. Do we need an iframe? - // Is pushState desired ... is it available? - this.options = _.extend({}, {root: '/'}, this.options, options); - this._wantsHashChange = this.options.hashChange !== false; - this._wantsPushState = !!this.options.pushState; - this._hasPushState = !!(this.options.pushState && window.history && window.history.pushState); - var fragment = this.getFragment(); - var docMode = document.documentMode; - var oldIE = (isExplorer.exec(navigator.userAgent.toLowerCase()) && (!docMode || docMode <= 7)); - - if (oldIE) { - this.iframe = $('"; - - this.dialog = new cdb.ui.common.ShareDialog({ - title: data.map.get("title"), - description: data.map.get("description"), - model: this.options.vis.map, - code: code, - url: data.url, - public_map_url: public_map_url, - share_url: data.share_url, - template: template, - target: $(".cartodb-share a"), - size: $(document).width() > 400 ? "" : "small", - width: $(document).width() > 400 ? 430 : 216 - }); - - $(".cartodb-map-wrapper").append(this.dialog.render().$el); - - }, - - render: function() { - - this.$el.html(this.template(_.extend(this.model.attributes))); - - return this; - - } - -}); -/** - * View to control the zoom of the map. - * - * Usage: - * - * var zoomControl = new cdb.geo.ui.Zoom({ model: map }); - * mapWrapper.$el.append(zoomControl.render().$el); - * - */ - - -cdb.geo.ui.Zoom = cdb.core.View.extend({ - - className: "cartodb-zoom", - - events: { - 'click .zoom_in': 'zoom_in', - 'click .zoom_out': 'zoom_out' - }, - - default_options: { - timeout: 0, - msg: '' - }, - - initialize: function() { - this.map = this.model; - - _.defaults(this.options, this.default_options); - - this.template = this.options.template ? this.options.template : cdb.templates.getTemplate('geo/zoom'); - this.map.bind('change:zoom change:minZoom change:maxZoom', this._checkZoom, this); - }, - - render: function() { - this.$el.html(this.template(this.options)); - this._checkZoom(); - return this; - }, - - _checkZoom: function() { - var zoom = this.map.get('zoom'); - this.$('.zoom_in')[ zoom < this.map.get('maxZoom') ? 'removeClass' : 'addClass' ]('disabled') - this.$('.zoom_out')[ zoom > this.map.get('minZoom') ? 'removeClass' : 'addClass' ]('disabled') - }, - - zoom_in: function(ev) { - if (this.map.get("maxZoom") > this.map.getZoom()) { - this.map.setZoom(this.map.getZoom() + 1); - } - ev.preventDefault(); - ev.stopPropagation(); - }, - - zoom_out: function(ev) { - if (this.map.get("minZoom") < this.map.getZoom()) { - this.map.setZoom(this.map.getZoom() - 1); - } - ev.preventDefault(); - ev.stopPropagation(); - } - -}); -/** - * View to know which is the map zoom. - * - * Usage: - * - * var zoomInfo = new cdb.geo.ui.ZoomInfo({ model: map }); - * mapWrapper.$el.append(zoomInfo.render().$el); - * - */ - - -cdb.geo.ui.ZoomInfo = cdb.core.View.extend({ - - className: "cartodb-zoom-info", - - initialize: function() { - this.model.bind("change:zoom", this.render, this); - }, - - render: function() { - this.$el.html(this.model.get("zoom")); - return this; - } -}); -/* - * Model for the legend item - * - * */ - -cdb.geo.ui.LegendItemModel = cdb.core.Model.extend({ - - defaults: { - name: "Untitled", - visible:true, - value: "" - } - -}); - -/* - * Collection of items for a legend - * - * */ - -cdb.geo.ui.LegendItems = Backbone.Collection.extend({ - model: cdb.geo.ui.LegendItemModel -}); - -/* - * Legend item - * - * */ - -cdb.geo.ui.LegendItem = cdb.core.View.extend({ - - tagName: "li", - - initialize: function() { - - _.bindAll(this, "render"); - - this.template = this.options.template ? _.template(this.options.template) : cdb.templates.getTemplate('geo/legend'); - - }, - - render: function() { - - var value; - - if (this.model.get("type") == 'image' && this.model.get("value")) { - value = "url( " + this.model.get("value") + ")"; - } else { - value = this.model.get("value"); - } - - var options = _.extend( this.model.toJSON(), { value: value }); - - this.$el.html(this.template(options)); - - return this.$el; - - } - -}); - -/* - * Legend View: wrapper for the different types of lengeds - * - * */ - -cdb.geo.ui.Legend = cdb.core.View.extend({ - - className: "cartodb-legend", - - events: { - - "dragstart": "_stopPropagation", - "mousedown": "_stopPropagation", - "touchstart": "_stopPropagation", - "MSPointerDown": "_stopPropagation", - "dblclick": "_stopPropagation", - "mousewheel": "_stopPropagation", - "DOMMouseScroll": "_stopPropagation", - "dbclick": "_stopPropagation", - "click": "_stopPropagation" - - }, - - initialize: function() { - - _.bindAll(this, "render", "show", "hide"); - - _.defaults(this.options, this.default_options); - - this.map = this.options.map; - - this._setupModel(); - this._setupItems(); - - this._updateLegendType(); - - }, - - _stopPropagation: function(ev) { - - ev.stopPropagation(); - - }, - - _setupModel: function() { - - if (!this.model) { - - this.model = new cdb.geo.ui.LegendModel({ - type: this.options.type || cdb.geo.ui.LegendModel.prototype.defaults.type, - title: this.options.title || cdb.geo.ui.LegendModel.prototype.defaults.title, - show_title: this.options.show_title || cdb.geo.ui.LegendModel.prototype.defaults.show_title, - template: this.options.template || cdb.geo.ui.LegendModel.prototype.defaults.template - }); - - } - - this.add_related_model(this.model); - - //this.model.bind("change:template change:type change:items change:title change:show_title", this._updateLegendType, this); - this.model.bind("change", this._updateLegendType, this); - - }, - - _updateLegendType: function() { - - var type = this.model.get("type"); - - this.legend_name = this._capitalize(type) + "Legend"; - - if (type == 'none' || type == null) { - - this.legend_name = null; - this.model.set({ type: "none" }, { silent: true }); - - } else if (!cdb.geo.ui[this.legend_name]) { - - // set the previous type - this.legend_name = null; - this.model.set({ type: this.model.previous("type") }, { silent: true }); - return; - - } - - this._refresh(); - - }, - - _refresh: function() { - - var self = this; - - if (this.view) this.view.clean(); - - var type = this.model.get("type"); - var title = this.model.get("title"); - var show_title = this.model.get("show_title"); - var template = this.model.get("template"); - - if (type && this.legend_name) { - - this.view = new cdb.geo.ui[this.legend_name]({ - model: this.model - }); - - // Set the type as the element class for styling - this.$el.removeClass(); - this.$el.addClass(this.className + " " + this.model.get("type")); - - this.show(); - - } else { - - this.hide(); - - this.$el.removeClass(); - this.$el.addClass(this.className + " none"); - - } - - this.render(); - - }, - - _setupItems: function() { - - var self = this; - - this.items = this.model.items; - - if (this.options.data) { - this.items.reset(this.options.data); - } - - this.items.bind("add remove change:value change:name", this.render, this); - - }, - - show: function(callback) { - var type = this.model.get("type"); - if (type && type != "none") this.$el.show(); - }, - - hide: function(callback) { - if (this.model.get("type")) this.$el.hide(); - }, - - _capitalize: function(string) { - if (string && _.isString(string)) { - return string.charAt(0).toUpperCase() + string.slice(1); - } - }, - - render: function() { - - if (this.view) { - - if (this.model.get("template")) { - this.$el.html(this.view.render().$el.html()); - this.$el.removeClass(this.model.get("type")) - this.$el.addClass("wrapper"); - } else { - this.$el.html(this.view.render().$el.html()); - } - } - - return this; - } - -}); - -/* - * DebugLegend - * - * */ -cdb.geo.ui.DebugLegend = cdb.core.View.extend({ }); - -/* - * BaseLegend: common methods for all the legends - * - * */ -cdb.geo.ui.BaseLegend = cdb.core.View.extend({ - - _bindModel: function() { - - this.model.bind("change:template change:title change:show_title", this.render, this); - - }, - - addTo: function(element) { - $(element).html(this.render().$el); - }, - - setTitle: function(title) { - this.model.set("title", title); - }, - - showTitle: function() { - this.model.set("show_title", true); - }, - - hideTitle: function() { - this.model.set("show_title", false); - } - -}); - -/* - * NoneLegend - * - * */ -cdb.geo.ui.NoneLegend = cdb.geo.ui.BaseLegend.extend({ }); -cdb.geo.ui.Legend.None = cdb.core.View.extend({ }); - -/* - * ChoroplethLegend - * - * */ -cdb.geo.ui.ChoroplethLegend = cdb.geo.ui.BaseLegend.extend({ - - className: "choropleth-legend", - - template: _.template('<% if (title && show_title) { %>\n
<%= title %>
<% } %>
  • \t\t<%= leftLabel %>
  • \t\t<%= rightLabel %>
  • \t
    <%= colors %>\n\t
'), - - initialize: function() { - - this.items = this.model.items; - - }, - - _generateColorList: function() { - - var colors = ""; - - if (this.model.get("colors")) { - return _.map(this.model.get("colors"), function(color) { - return '\n\t
'; - }).join(""); - } else { - - for (var i = 2; i < this.items.length; i++) { - var color = this.items.at(i).get("value"); - colors += '\n\t
'; - } - } - - return colors; - - }, - - setLeftLabel: function(text) { - - this.model.set("leftLabel", text); - - }, - - setRightLabel: function(text) { - - this.model.set("rightLabel", text); - - }, - - setColors: function(colors) { - - this.model.set("colors", colors); - - }, - - render: function() { - - if (this.model.get("template")) { - - var template = _.template(this.model.get("template")); - this.$el.html(template(this.model.toJSON())); - - } else { - - - if (this.items.length >= 2) { - - this.leftLabel = this.items.at(0); - this.rightLabel = this.items.at(1); - - var leftLabel = this.model.get("leftLabel") || this.leftLabel.get("value"); - var rightLabel = this.model.get("rightLabel") || this.rightLabel.get("value"); - - var colors = this._generateColorList(); - - var options = _.extend( this.model.toJSON(), { leftLabel: leftLabel, rightLabel: rightLabel, colors: colors, buckets_count: colors.length }); - - this.$el.html(this.template(options)); - } - } - - return this; - - } - -}); - -/* - * DensityLegend - * - * */ -cdb.geo.ui.DensityLegend = cdb.geo.ui.BaseLegend.extend({ - - className: "density-legend", - - template: _.template('<% if (title && show_title) { %>\n
<%= title %>
<% } %>
  • \t<%= leftLabel %>
  • \t<%= rightLabel %>
  • \t
    <%= colors %>\n\t
'), - - initialize: function() { - - this.items = this.model.items; - - }, - - setLeftLabel: function(text) { - - this.model.set("leftLabel", text); - - }, - - setRightLabel: function(text) { - - this.model.set("rightLabel", text); - - }, - - setColors: function(colors) { - - this.model.set("colors", colors); - - }, - - _generateColorList: function() { - - var colors = ""; - - if (this.model.get("colors")) { - - return _.map(this.model.get("colors"), function(color) { - return '\n\t\t
'; - }).join(""); - - } else { - - for (var i = 2; i < this.items.length; i++) { - var color = this.items.at(i).get("value"); - colors += '\n\t\t
'; - } - } - - return colors; - - }, - - - render: function() { - - if (this.model.get("template")) { - - var template = _.template(this.model.get("template")); - this.$el.html(template(this.model.toJSON())); - - } else { - - if (this.items.length >= 2) { - - this.leftLabel = this.items.at(0); - this.rightLabel = this.items.at(1); - - var leftLabel = this.model.get("leftLabel") || this.leftLabel.get("value"); - var rightLabel = this.model.get("rightLabel") || this.rightLabel.get("value"); - - var colors = this._generateColorList(); - - var options = _.extend( this.model.toJSON(), { leftLabel: leftLabel, rightLabel: rightLabel, colors: colors, buckets_count: colors.length }); - - this.$el.html(this.template(options)); - } - } - - return this; - - } - -}); - -/* - * Density Legend public interface - * - * */ -cdb.geo.ui.Legend.Density = cdb.geo.ui.DensityLegend.extend({ - - type: "density", - - className: "cartodb-legend density", - - initialize: function() { - - this.items = this.options.items; - - this.model = new cdb.geo.ui.LegendModel({ - type: this.type, - title: this.options.title, - show_title: this.options.title ? true : false, - leftLabel: this.options.left || this.options.leftLabel, - rightLabel: this.options.right || this.options.rightLabel, - colors: this.options.colors, - buckets_count: this.options.colors ? this.options.colors.length : 0, - items: this.options.items - }); - - this._bindModel(); - - }, - - _bindModel: function() { - - this.model.bind("change:colors change:template change:title change:show_title change:colors change:leftLabel change:rightLabel", this.render, this); - - }, - - _generateColorList: function() { - - return _.map(this.model.get("colors"), function(color) { - return '
'; - }).join(""); - - }, - - render: function() { - - var options = _.extend(this.model.toJSON(), { colors: this._generateColorList() }); - - this.$el.html(this.template(options)); - - return this; - - } - -}); - -/* - * IntensityLegend - * - * */ -cdb.geo.ui.IntensityLegend = cdb.geo.ui.BaseLegend.extend({ - - className: "intensity-legend", - - template: _.template('<% if (title && show_title) { %>\n
<%= title %>
<% } %>
  • \t<%= leftLabel %>
  • \t<%= rightLabel %>
'), - - initialize: function() { - - this.items = this.model.items; - - }, - - _bindModel: function() { - - this.model.bind("change:template", this.render, this); - - }, - - setColor: function(color) { - - this.model.set("color", color); - - }, - - setLeftLabel: function(text) { - - this.model.set("leftLabel", text); - - }, - - setRightLabel: function(text) { - - this.model.set("rightLabel", text); - - }, - - _hexToRGB: function(hex) { - - var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); - - return result ? { - r: parseInt(result[1], 16), - g: parseInt(result[2], 16), - b: parseInt(result[3], 16) - } : null; - - }, - - _rgbToHex: function(r, g, b) { - - function componentToHex(c) { - var hex = c.toString(16); - return hex.length == 1 ? "0" + hex : hex; - } - - return "#" + componentToHex(r) + componentToHex(g) + componentToHex(b); - }, - - _calculateMultiply: function(color, steps) { - - var colorHex = this._hexToRGB(color); - - if (colorHex) { - - var r = colorHex.r; - var g = colorHex.g; - var b = colorHex.b; - - for (var i = 0; i <= steps; i++) { - r = Math.round(r * colorHex.r/255); - g = Math.round(g * colorHex.g/255); - b = Math.round(b * colorHex.b/255); - } - - return this._rgbToHex(r,g,b); - - } - - return "#ffffff"; - - }, - - _renderGraph: function(baseColor) { - - var s = ""; - - s+= "background: <%= color %>;"; - s+= "background: -moz-linear-gradient(left, <%= color %> 0%, <%= right %> 100%);"; - s+= "background: -webkit-gradient(linear, left top, right top, color-stop(0%,<%= color %>), color-stop(100%,<%= right %>));"; - s+= "background: -webkit-linear-gradient(left, <%= color %> 0%,<%= right %> 100%);"; - s+= "background: -o-linear-gradient(left, <%= color %> 0%,<%= right %> 100%);"; - s+= "background: -ms-linear-gradient(left, <%= color %> 0%,<%= right %> 100%)"; - s+= "background: linear-gradient(to right, <%= color %> 0%,<%= right %> 100%);"; - s+= "filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='<%= color %>', endColorstr='<%= right %>',GradientType=1 );"; - s+= "background-image: -ms-linear-gradient(left, <%= color %> 0%,<%= right %> 100%)"; - - var backgroundStyle = _.template(s); - - var multipliedColor = this._calculateMultiply(baseColor, 4); - - this.$el.find(".graph").attr("style", backgroundStyle({ color: baseColor, right: multipliedColor })); - - }, - - render: function() { - - if (this.model.get("template")) { - - var template = _.template(this.model.get("template")); - this.$el.html(template(this.model.toJSON())); - - } else { - - if (this.items.length >= 3) { - - this.leftLabel = this.items.at(0); - this.rightLabel = this.items.at(1); - var color = this.model.get("color") || this.items.at(2).get("value"); - - var leftLabel = this.model.get("leftLabel") || this.leftLabel.get("value"); - var rightLabel = this.model.get("rightLabel") || this.rightLabel.get("value"); - - var options = _.extend( this.model.toJSON(), { color: color, leftLabel: leftLabel, rightLabel: rightLabel }); - - this.$el.html(this.template(options)); - - this._renderGraph(color); - } - - } - - return this; - - } - -}); - -/* - * CategoryLegend - * - * */ -cdb.geo.ui.CategoryLegend = cdb.geo.ui.BaseLegend.extend({ - - className: "category-legend", - - template: _.template('<% if (title && show_title) { %>\n
<%= title %>
<% } %>
    '), - - initialize: function() { - - this.items = this.model.items; - - }, - - _bindModel: function() { - - this.model.bind("change:title change:show_title change:template", this.render, this); - - }, - - _renderItems: function() { - - this.items.each(this._renderItem, this); - - }, - - _renderItem: function(item) { - - view = new cdb.geo.ui.LegendItem({ - model: item, - className: (item.get("value") && item.get("value").indexOf("http") >= 0 || item.get("type") && item.get("type") == 'image') ? "bkg" : "", - template: '\t\t
    <%= name || ((name === false) ? "false": "null") %>' - }); - - this.$el.find("ul").append(view.render()); - - }, - - render: function() { - - if (this.model.get("template")) { - - var template = _.template(this.model.get("template")); - this.$el.html(template(this.model.toJSON())); - - } else { - - this.$el.html(this.template(this.model.toJSON())); - - if (this.items.length > 0) { - this._renderItems(); - } else { - this.$el.html('
    The category legend is empty
    '); - } - } - - return this; - - } - -}); - -/* - * Category Legend public interface - * - * */ -cdb.geo.ui.Legend.Category = cdb.geo.ui.CategoryLegend.extend({ - - className: "cartodb-legend category", - - type: "category", - - initialize: function() { - - this.items = new cdb.geo.ui.LegendItems(this.options.data); - - this.model = new cdb.geo.ui.LegendModel({ - type: this.type, - title: this.options.title, - show_title: this.options.title ? true : false - }); - - this._bindModel(); - - }, - - render: function() { - - this.$el.html(this.template(this.model.toJSON())); - - this._renderItems(); - - return this; - - } - -}); - -/* - * ColorLegend - * - * */ -cdb.geo.ui.ColorLegend = cdb.geo.ui.BaseLegend.extend({ - - className: "color-legend", - - type: "color", - - template: _.template('<% if (title && show_title) { %>\n
    <%= title %>
    <% } %>
      '), - - initialize: function() { - - this.items = this.model.items; - - }, - - _renderItems: function() { - - this.items.each(this._renderItem, this); - - }, - - _renderItem: function(item) { - - view = new cdb.geo.ui.LegendItem({ - model: item, - className: (item.get("value") && item.get("value").indexOf("http") >= 0) ? "bkg" : "", - template: '\t\t
      <%= name || ((name === false) ? "false": "null") %>' - }); - - this.$el.find("ul").append(view.render()); - - }, - - render: function() { - - this.$el.html(this.template(this.model.toJSON())); - - if (this.items.length > 0) { - this._renderItems(); - } else { - this.$el.html('
      The color legend is empty
      '); - } - - return this; - - } - -}); - -/* - * Color Legend public interface - * - * */ -cdb.geo.ui.Legend.Color = cdb.geo.ui.Legend.Category.extend({ }); - -/* - * StackedLegend - * - * */ -cdb.geo.ui.StackedLegend = cdb.core.View.extend({ - - events: { - - "dragstart": "_stopPropagation", - "mousedown": "_stopPropagation", - "touchstart": "_stopPropagation", - "MSPointerDown": "_stopPropagation", - "dblclick": "_stopPropagation", - "mousewheel": "_stopPropagation", - "DOMMouseScroll": "_stopPropagation", - "dbclick": "_stopPropagation", - "click": "_stopPropagation" - - }, - - className: "cartodb-legend-stack", - - initialize: function() { - - _.each(this.options.legends, this._setupBinding, this); - - }, - - _stopPropagation: function(ev) { - - ev.stopPropagation(); - - }, - - //TODO: change this method to - // getLegendByIndex - getLegendByIndex: function(index) { - if (!this._layerByIndex) { - this._layerByIndex = {}; - var legends = this.options.legends; - for (var i = 0; i < legends.length; ++i) { - var legend = legends[i]; - this._layerByIndex[legend.options.index] = legend; - } - } - return this._layerByIndex[index]; - }, - - _setupBinding: function(legend) { - - legend.model.bind("change:type", this._checkVisibility, this); - this.add_related_model(legend.model); - - }, - - _checkVisibility: function() { - - var visible = _.some(this.options.legends, function(legend) { - return legend.model.get("type") && (legend.model.get("type") != "none" || legend.model.get("template")) - }, this); - - if (visible) { - this.show(); - } else { - this.hide(); - } - - _.each(this.options.legends, function(item) { - - var type = item.model.get("type"); - - if (type && type != "none") { - item.show(); - } else { - item.hide(); - } - - }, this); - - - }, - - _renderItems: function() { - - _.each(this.options.legends, function(item) { - this.$el.append(item.render().$el); - }, this); - - }, - - show: function() { - this.$el.show(); - }, - - hide: function() { - this.$el.hide(); - }, - - addTo: function(element) { - $(element).html(this.render().$el); - }, - - render: function() { - - this._renderItems(); - this._checkVisibility(); - - return this; - - } - -}); - -cdb.geo.ui.Legends = Backbone.Collection.extend({ - model: cdb.geo.ui.LegendModel -}); - -/* - * Stacked Legend public interface - * - * */ -cdb.geo.ui.Legend.Stacked = cdb.geo.ui.StackedLegend.extend({ - - initialize: function() { - - if (this.options.legends) { - - var legendModels = _.map(this.options.legends, function(legend) { - return legend.model; - }); - - this.legendItems = new cdb.geo.ui.Legends(legendModels); - - this.legendItems.bind("add remove change", this.render, this); - - } else if (this.options.data) { - - var legendModels = _.map(this.options.data, function(legend) { - return new cdb.geo.ui.LegendModel(legend); - }); - - this.legendItems = new cdb.geo.ui.Legends(legendModels); - - this.legendItems.bind("add remove change", this.render, this); - - } - - }, - - _capitalize: function(string) { - if (string && _.isString(string)) { - return string.charAt(0).toUpperCase() + string.slice(1); - } - }, - - render: function() { - - this.$el.empty(); - - this.legends = []; - - if (this.legendItems && this.legendItems.length > 0) { - - this.legendItems.each(this._renderLegend, this); - - } - - return this; - - }, - - _renderLegend: function(model) { - - var type = model.get("type"); - - if (!type) type = "custom"; - - type = this._capitalize(type); - - var view = new cdb.geo.ui.Legend[type](model.attributes); - - this.legends.push(view); - - if (model.get("visible") !== false) this.$el.append(view.render().$el); - - }, - - getLegendAt: function(n) { - - return this.legends[n]; - - }, - - addLegend: function(attributes) { - - var legend = new cdb.geo.ui.LegendModel(attributes); - this.legendItems.push(legend); - - }, - - removeLegendAt: function(n) { - - var legend = this.legendItems.at(n); - this.legendItems.remove(legend); - - } - -}); - - -/* - * Legend Model - * - * */ -cdb.geo.ui.LegendModel = cdb.core.Model.extend({ - - defaults: { - type: null, - show_title: false, - title: "", - template: "" - }, - - initialize: function() { - - this.items = new cdb.geo.ui.LegendItems(this.get("items")); - - this.items.bind("add remove reset change", function() { - this.set({ items: this.items.toJSON() }); - }, this); - - this.bind("change:items", this._onUpdateItems, this); - this.bind("change:title change:show_title", this._onUpdateTitle, this); - this.bind("change:template", this._onUpdateTemplate, this); - - }, - - _onUpdateTemplate: function() { - this.template = this.get("template"); - }, - - _onUpdateTitle: function() { - this.title = this.get("title"); - this.show_title = this.get("show_title"); - }, - - _onUpdateItems: function() { - var items = this.get("items"); - this.items.reset(items); - } - -}); - -/* - * CustomLegend - * - * */ -cdb.geo.ui.CustomLegend = cdb.geo.ui.BaseLegend.extend({ - - className: "custom-legend", - type: "custom", - - template: _.template('<% if (title && show_title) { %>\n
      <%= title %>
      <% } %>
        '), - - initialize: function() { - - this.items = this.model.items; - - }, - - setData: function(data) { - - this.items = new cdb.geo.ui.LegendItems(data); - this.model.items = this.items; - this.model.set("items", data); - - }, - - _renderItems: function() { - - this.items.each(this._renderItem, this); - - }, - - _renderItem: function(item) { - - view = new cdb.geo.ui.LegendItem({ - model: item, - className: (item.get("value") && item.get("value").indexOf("http") >= 0) ? "bkg" : "", - template: '\t\t
        \n\t\t<%= name || "null" %>' - }); - - this.$el.find("ul").append(view.render()); - - }, - - render: function() { - - if (this.model.get("template")) { - - var template = _.template(this.model.get("template")); - this.$el.html(template(this.model.toJSON())); - - } else { - - this.$el.html(this.template(this.model.toJSON())); - - if (this.items.length > 0) { - this._renderItems(); - } else { - this.$el.html('
        The legend is empty
        '); - } - } - - return this; - - } - -}); - -/* - * Custom Legend public interface - * - * */ -cdb.geo.ui.Legend.Custom = cdb.geo.ui.CustomLegend.extend({ - - className: "cartodb-legend custom", - - type: "custom", - - initialize: function() { - - this.items = new cdb.geo.ui.LegendItems(this.options.data || this.options.items); - - this.model = new cdb.geo.ui.LegendModel({ - type: this.type, - title: this.options.title, - show_title: this.options.title ? true : false, - items: this.items.models - }); - - this._bindModel(); - - }, - - _bindModel: function() { - - this.model.bind("change:items change:template change:title change:show_title", this.render, this); - - } - -}); - -/* - * BubbleLegend - * - * */ -cdb.geo.ui.BubbleLegend = cdb.geo.ui.BaseLegend.extend({ - - className: "bubble-legend", - - template: _.template('<% if (title && show_title) { %>\n
        <%= title %>
        <% } %>
        • \t<%= min %>
        • \t\t
        • \t<%= max %>
        '), - - initialize: function() { - - this.items = this.model.items; - - }, - - _bindModel: function() { - - this.model.bind("change:template change:title change:show_title change:color change:min change:max", this.render, this); - - }, - - setColor: function(color) { - this.model.set("color", color); - }, - - setMinValue: function(value) { - this.model.set("min", value); - }, - - setMaxValue: function(value) { - this.model.set("max", value); - }, - - _renderGraph: function(color) { - this.$el.find(".graph").css("background", color); - }, - - render: function() { - - if (this.model.get("template")) { - - var template = _.template(this.model.get("template")); - this.$el.html(template(this.model.toJSON())); - - this.$el.removeClass("bubble-legend"); - - } else { - - var color = this.model.get("color") || (this.items.length >= 3 ? this.items.at(2).get("value") : ""); - - if (this.items.length >= 3) { - - var min = this.model.get("min") || this.items.at(0).get("value"); - var max = this.model.get("max") || this.items.at(1).get("value"); - - var options = _.extend(this.model.toJSON(), { min: min, max: max }); - - this.$el.html(this.template(options)); - - } - - this._renderGraph(color); - } - - return this; - - } - -}); - - -/* - * Bubble Legend public interface - * - * */ -cdb.geo.ui.Legend.Bubble = cdb.geo.ui.BubbleLegend.extend({ - - className: "cartodb-legend bubble", - - type: "bubble", - - initialize: function() { - - this.model = new cdb.geo.ui.LegendModel({ - type: this.type, - title: this.options.title, - min: this.options.min, - max: this.options.max, - color: this.options.color, - show_title: this.options.title ? true : false - }); - - this.add_related_model(this.model); - - this._bindModel(); - - }, - - render: function() { - - this.$el.html(this.template(this.model.toJSON())); - - this._renderGraph(this.model.get("color")); - - return this; - - } - -}); - -/* - * Choropleth Legend public interface - * - * */ -cdb.geo.ui.Legend.Choropleth = cdb.geo.ui.ChoroplethLegend.extend({ - - type: "choropleth", - - className: "cartodb-legend choropleth", - - initialize: function() { - - this.items = this.options.items; - - this.model = new cdb.geo.ui.LegendModel({ - type: this.type, - title: this.options.title, - show_title: this.options.title ? true : false, - leftLabel: this.options.left || this.options.leftLabel, - rightLabel: this.options.right || this.options.rightLabel, - colors: this.options.colors, - buckets_count: this.options.colors ? this.options.colors.length : 0 - }); - - this.add_related_model(this.model); - this._bindModel(); - - }, - - _bindModel: function() { - - this.model.bind("change:template change:title change:show_title change:colors change:leftLabel change:rightLabel", this.render, this); - - }, - - _generateColorList: function() { - - return _.map(this.model.get("colors"), function(color) { - return '\t\t
        '; - }).join(""); - - }, - - render: function() { - - var options = _.extend(this.model.toJSON(), { colors: this._generateColorList() }); - - this.$el.html(this.template(options)); - - return this; - - } - -}); - - -/* - * Intensity Legend public interface - * - * */ -cdb.geo.ui.Legend.Intensity = cdb.geo.ui.IntensityLegend.extend({ - - className: "cartodb-legend intensity", - type: "intensity", - - initialize: function() { - - this.items = this.options.items; - - this.model = new cdb.geo.ui.LegendModel({ - type: this.type, - title: this.options.title, - show_title: this.options.title ? true : false, - color: this.options.color, - leftLabel: this.options.left || this.options.leftLabel, - rightLabel: this.options.right || this.options.rightLabel - }); - - this.add_related_model(this.model); - this._bindModel(); - - }, - - _bindModel: function() { - - this.model.bind("change:title change:show_title change:color change:leftLabel change:rightLabel", this.render, this); - - }, - - render: function() { - - this.$el.html(this.template(this.model.toJSON())); - - this._renderGraph(this.model.get("color")); - - return this; - - } - -}); -cdb.geo.ui.SwitcherItemModel = Backbone.Model.extend({ }); - -cdb.geo.ui.SwitcherItems = Backbone.Collection.extend({ - model: cdb.geo.ui.SwitcherItemModel -}); - -cdb.geo.ui.SwitcherItem = cdb.core.View.extend({ - - tagName: "li", - - events: { - - "click a" : "select" - - }, - - initialize: function() { - - _.bindAll(this, "render"); - this.template = cdb.templates.getTemplate('templates/map/switcher/item'); - this.parent = this.options.parent; - this.model.on("change:selected", this.render); - - }, - - select: function(e) { - e.preventDefault(); - this.parent.toggle(this); - var callback = this.model.get("callback"); - - if (callback) { - callback(); - } - - }, - - render: function() { - - if (this.model.get("selected") == true) { - this.$el.addClass("selected"); - } else { - this.$el.removeClass("selected"); - } - - this.$el.html(this.template(this.model.toJSON())); - return this.$el; - - } - -}); - -cdb.geo.ui.Switcher = cdb.core.View.extend({ - - id: "switcher", - - default_options: { - - }, - - initialize: function() { - - this.map = this.model; - - this.add_related_model(this.model); - - _.bindAll(this, "render", "show", "hide", "toggle"); - - _.defaults(this.options, this.default_options); - - if (this.collection) { - this.model.collection = this.collection; - } - - this.template = this.options.template ? this.options.template : cdb.templates.getTemplate('geo/switcher'); - }, - - show: function() { - this.$el.fadeIn(250); - }, - - hide: function() { - this.$el.fadeOut(250); - }, - - toggle: function(clickedItem) { - - if (this.collection) { - this.collection.each(function(item) { - item.set("selected", !item.get("selected")); - }); - } - - }, - - render: function() { - var self = this; - - if (this.model != undefined) { - this.$el.html(this.template(this.model.toJSON())); - } - - if (this.collection) { - - this.collection.each(function(item) { - - var view = new cdb.geo.ui.SwitcherItem({ parent: self, className: item.get("className"), model: item }); - self.$el.find("ul").append(view.render()); - - }); - } - - return this; - } - -}); -/** Usage: - * - * Add Infowindow model: - * - * var infowindowModel = new cdb.geo.ui.InfowindowModel({ - * template_name: 'infowindow_light', - * latlng: [72, -45], - * offset: [100, 10] - * }); - * - * var infowindow = new cdb.geo.ui.Infowindow({ - * model: infowindowModel, - * mapView: mapView - * }); - * - * Show the infowindow: - * infowindow.showInfowindow(); - * - */ - -cdb.geo.ui.InfowindowModel = Backbone.Model.extend({ - - SYSTEM_COLUMNS: ['the_geom', 'the_geom_webmercator', 'created_at', 'updated_at', 'cartodb_id', 'cartodb_georef_status'], - - defaults: { - template_name: 'infowindow_light', - latlng: [0, 0], - offset: [28, 0], // offset of the tip calculated from the bottom left corner - maxHeight: 180, // max height of the content, not the whole infowindow - autoPan: true, - template: "", - content: "", - visibility: false, - alternative_names: { }, - fields: null // contains the fields displayed in the infowindow - }, - - clearFields: function() { - this.set({fields: []}); - }, - - saveFields: function(where) { - where = where || 'old_fields'; - this.set(where, _.clone(this.get('fields'))); - }, - - fieldCount: function() { - var fields = this.get('fields') - if (!fields) return 0; - return fields.length - }, - - restoreFields: function(whiteList, from) { - from = from || 'old_fields'; - var fields = this.get(from); - if(whiteList) { - fields = fields.filter(function(f) { - return _.contains(whiteList, f.name); - }); - } - if(fields && fields.length) { - this._setFields(fields); - } - this.unset(from); - }, - - _cloneFields: function() { - return _(this.get('fields')).map(function(v) { - return _.clone(v); - }); - }, - - _setFields: function(f) { - f.sort(function(a, b) { return a.position - b.position; }); - this.set({'fields': f}); - }, - - sortFields: function() { - this.get('fields').sort(function(a, b) { return a.position - b.position; }); - }, - - _addField: function(fieldName, at) { - var dfd = $.Deferred(); - if(!this.containsField(fieldName)) { - var fields = this.get('fields'); - if(fields) { - at = at === undefined ? fields.length: at; - fields.push({ name: fieldName, title: true, position: at }); - } else { - at = at === undefined ? 0 : at; - this.set('fields', [{ name: fieldName, title: true, position: at }], { silent: true}); - } - } - dfd.resolve(); - return dfd.promise(); - }, - - addField: function(fieldName, at) { - var self = this; - $.when(this._addField(fieldName, at)).then(function() { - self.sortFields(); - self.trigger('change:fields'); - self.trigger('add:fields'); - }); - return this; - }, - - getFieldProperty: function(fieldName, k) { - if(this.containsField(fieldName)) { - var fields = this.get('fields') || []; - var idx = _.indexOf(_(fields).pluck('name'), fieldName); - return fields[idx][k]; - } - return null; - }, - - setFieldProperty: function(fieldName, k, v) { - if(this.containsField(fieldName)) { - var fields = this._cloneFields() || []; - var idx = _.indexOf(_(fields).pluck('name'), fieldName); - fields[idx][k] = v; - this._setFields(fields); - } - return this; - }, - - getAlternativeName: function(fieldName) { - return this.get("alternative_names") && this.get("alternative_names")[fieldName]; - }, - - setAlternativeName: function(fieldName, alternativeName) { - var alternativeNames = this.get("alternative_names") || []; - - alternativeNames[fieldName] = alternativeName; - this.set({ 'alternative_names': alternativeNames }); - this.trigger('change:alternative_names'); - }, - - getFieldPos: function(fieldName) { - var p = this.getFieldProperty(fieldName, 'position'); - if(p == undefined) { - return Number.MAX_VALUE; - } - return p; - }, - - containsAlternativeName: function(fieldName) { - var names = this.get('alternative_names') || []; - return names[fieldName]; - }, - - containsField: function(fieldName) { - var fields = this.get('fields') || []; - return _.contains(_(fields).pluck('name'), fieldName); - }, - - removeField: function(fieldName) { - if(this.containsField(fieldName)) { - var fields = this._cloneFields() || []; - var idx = _.indexOf(_(fields).pluck('name'), fieldName); - if(idx >= 0) { - fields.splice(idx, 1); - } - this._setFields(fields); - this.trigger('remove:fields') - } - return this; - }, - - // updates content with attributes - updateContent: function(attributes) { - var fields = this.get('fields'); - this.set('content', cdb.geo.ui.InfowindowModel.contentForFields(attributes, fields)); - } - -}, { - contentForFields: function(attributes, fields, options) { - options = options || {}; - var render_fields = []; - for(var j = 0; j < fields.length; ++j) { - var f = fields[j]; - var value = String(attributes[f.name]); - if(options.empty_fields || (attributes[f.name] !== undefined && value != "")) { - render_fields.push({ - title: f.title ? f.name : null, - value: attributes[f.name], - index: j ? j : null - }); - } - } - - // manage when there is no data to render - if (render_fields.length === 0) { - render_fields.push({ - title: null, - value: 'No data available', - index: j ? j : null, - type: 'empty' - }); - } - - return { - fields: render_fields, - data: attributes - }; - } -}); - -cdb.geo.ui.Infowindow = cdb.core.View.extend({ - className: "cartodb-infowindow", - - spin_options: { - lines: 10, length: 0, width: 4, radius: 6, corners: 1, rotate: 0, color: 'rgba(0,0,0,0.5)', - speed: 1, trail: 60, shadow: false, hwaccel: true, className: 'spinner', zIndex: 2e9, - top: 'auto', left: 'auto', position: 'absolute' - }, - - events: { - // Close bindings - "click .close": "_closeInfowindow", - "touchstart .close": "_closeInfowindow", - "MSPointerDown .close": "_closeInfowindow", - // Rest infowindow bindings - "dragstart": "_checkOrigin", - "mousedown": "_checkOrigin", - "touchstart": "_checkOrigin", - "MSPointerDown": "_checkOrigin", - "dblclick": "_stopPropagation", - "DOMMouseScroll": "_stopBubbling", - 'MozMousePixelScroll': "_stopBubbling", - "mousewheel": "_stopBubbling", - "dbclick": "_stopPropagation", - "click": "_stopPropagation" - }, - - initialize: function(){ - var self = this; - - _.bindAll(this, "render", "setLatLng", "_setTemplate", "_updatePosition", - "_update", "toggle", "show", "hide"); - - this.mapView = this.options.mapView; - - // Set template if it is defined in options - if (this.options.template) this.model.set('template', this.options.template); - - // Set template view variable and - // compile it if it is necessary - if (this.model.get('template')) { - this._compileTemplate(); - } else { - this._setTemplate(); - } - - this.model.bind('change:content', this.render, this); - this.model.bind('change:template_name', this._setTemplate, this); - this.model.bind('change:latlng', this._update, this); - this.model.bind('change:visibility', this.toggle, this); - this.model.bind('change:template', this._compileTemplate, this); - this.model.bind('change:alternative_names', this.render, this); - this.model.bind('change:width', this.render, this); - this.model.bind('change:maxHeight', this.render, this); - - this.mapView.map.bind('change', this._updatePosition, this); - - this.mapView.bind('zoomstart', function(){ - self.hide(true); - }); - - this.mapView.bind('zoomend', function() { - self.show(true); - }); - - this.add_related_model(this.mapView.map); - - // Hide the element - this.$el.hide(); - }, - - - /** - * Render infowindow content - */ - render: function() { - - if(this.template) { - - // If there is content, destroy the jscrollpane first, then remove the content. - var $jscrollpane = this.$(".cartodb-popup-content"); - if ($jscrollpane.length > 0 && $jscrollpane.data() != null) { - $jscrollpane.data().jsp && $jscrollpane.data().jsp.destroy(); - } - - // Clone fields and template name - var fields = _.map(this.model.attributes.content.fields, function(field){ - return _.clone(field); - }); - var data = this.model.get('content') ? this.model.get('content').data : {}; - - // If a custom template is not applied, let's sanitized - // fields for the template rendering - if (this.model.get('template_name')) { - var template_name = _.clone(this.model.attributes.template_name); - - // Sanitized them - fields = this._fieldsToString(fields, template_name); - } - - // Join plan fields values with content to work with - // custom infowindows and CartoDB infowindows. - var values = {}; - _.each(this.model.get('content').fields, function(pair) { - values[pair.title] = pair.value; - }) - - var obj = _.extend({ - content: { - fields: fields, - data: data - } - },values); - - this.$el.html(this.template(obj)); - - // Set width and max-height from the model only - // If there is no width set, we don't force our infowindow - if (this.model.get('width')) { - this.$('.cartodb-popup').css('width', this.model.get('width') + 'px'); - } - this.$('.cartodb-popup .cartodb-popup-content').css('max-height', this.model.get('maxHeight') + 'px'); - - // Hello jscrollpane hacks! - // It needs some time to initialize, if not it doesn't render properly the fields - // Check the height of the content + the header if exists - var self = this; - setTimeout(function() { - var actual_height = self.$(".cartodb-popup-content").outerHeight(); - if (self.model.get('maxHeight') <= actual_height) - self.$(".cartodb-popup-content").jScrollPane({ - maintainPosition: false, - verticalDragMinHeight: 20 - }); - }, 1); - - // If the infowindow is loading, show spin - this._checkLoading(); - - // If the template is 'cover-enabled', load the cover - this._loadCover(); - - if(!this.isLoadingData()) { - this.model.trigger('domready', this, this.$el); - this.trigger('domready', this, this.$el); - } - } - - return this; - }, - - _getModelTemplate: function() { - return this.model.get("template_name") - }, - - /** - * Change template of the infowindow - */ - _setTemplate: function() { - if (this.model.get('template_name')) { - this.template = cdb.templates.getTemplate(this._getModelTemplate()); - this.render(); - } - }, - - /** - * Compile template of the infowindow - */ - _compileTemplate: function() { - var template = this.model.get('template') ? - this.model.get('template') : - cdb.templates.getTemplate(this._getModelTemplate()); - - if(typeof(template) !== 'function') { - this.template = new cdb.core.Template({ - template: template, - type: this.model.get('template_type') || 'mustache' - }).asFunction() - } else { - this.template = template - } - - this.render(); - }, - - /** - * Check event origin - */ - _checkOrigin: function(ev) { - // If the mouse down come from jspVerticalBar - // dont stop the propagation, but if the event - // is a touchstart, stop the propagation - var come_from_scroll = (($(ev.target).closest(".jspVerticalBar").length > 0) && (ev.type != "touchstart")); - - if (!come_from_scroll) { - ev.stopPropagation(); - } - }, - - /** - * Convert values to string unless value is NULL - */ - _fieldsToString: function(fields, template_name) { - var fields_sanitized = []; - if (fields && fields.length > 0) { - var self = this; - fields_sanitized = _.map(fields, function(field,i) { - // Return whole attribute sanitized - return self._sanitizeField(field, template_name, field.index || i); - }); - } - return fields_sanitized; - }, - - /** - * Sanitize fields, what does it mean? - * - If value is null, transform to string - * - If value is an url, add it as an attribute - * - Cut off title if it is very long (in header or image templates). - * - If the value is a valid url, let's make it a link. - * - More to come... - */ - _sanitizeField: function(attr, template_name, pos) { - // Check null or undefined :| and set both to empty == '' - if (attr.value == null || attr.value == undefined) { - attr.value = ''; - } - - //Get the alternative title - var alternative_name = this.model.getAlternativeName(attr.title); - - if (attr.title && alternative_name) { - // Alternative title - attr.title = alternative_name; - } else if (attr.title) { - // Remove '_' character from titles - attr.title = attr.title.replace(/_/g,' '); - } - - // Cast all values to string due to problems with Mustache 0 number rendering - var new_value = attr.value.toString(); - - // If it is index 0, not any field type, header template type and length bigger than 30... cut off the text! - if (!attr.type && pos==0 && attr.value.length > 35 && template_name && template_name.search('_header_') != -1) { - new_value = attr.value.substr(0,32) + "..."; - } - - // If it is index 1, not any field type, header image template type and length bigger than 30... cut off the text! - if (!attr.type && pos==1 && attr.value.length > 35 && template_name && template_name.search('_header_with_image') != -1) { - new_value = attr.value.substr(0,32) + "..."; - } - - // Is it the value a link? - if (this._isValidURL(attr.value)) { - new_value = "" + new_value + "" - } - - // If it is index 0, not any field type, header image template type... don't cut off the text or add any link!! - if (pos==0 && template_name.search('_header_with_image') != -1) { - new_value = attr.value; - } - - // Save new sanitized value - attr.value = new_value; - - return attr; - }, - - isLoadingData: function() { - var content = this.model.get("content"); - return content.fields && content.fields.length == 1 && content.fields[0].type === "loading"; - }, - - /** - * Check if infowindow is loading the row content - */ - _checkLoading: function() { - if (this.isLoadingData()) { - this._startSpinner(); - } else { - this._stopSpinner(); - } - }, - - /** - * Stop loading spinner - */ - _stopSpinner: function() { - if (this.spinner) - this.spinner.stop() - }, - - /** - * Start loading spinner - */ - _startSpinner: function($el) { - this._stopSpinner(); - - var $el = this.$el.find('.loading'); - - if ($el) { - // Check if it is dark or other to change color - var template_dark = this.model.get('template_name').search('dark') != -1; - - if (template_dark) { - this.spin_options.color = '#FFF'; - } else { - this.spin_options.color = 'rgba(0,0,0,0.5)'; - } - - this.spinner = new Spinner(this.spin_options).spin(); - $el.append(this.spinner.el); - } - }, - - /** - * Stop loading spinner - */ - _containsCover: function() { - return this.$el.find(".cartodb-popup.header").attr("data-cover") ? true : false; - }, - - - /** - * Get cover URL - */ - _getCoverURL: function() { - var content = this.model.get("content"); - - if (content && content.fields && content.fields.length > 0) { - return (content.fields[0].value || '').toString(); - } - - return false; - }, - - /** - * Attempts to load the cover URL and show it - */ - _loadCover: function() { - - if (!this._containsCover()) return; - - var - self = this, - $cover = this.$(".cover"), - $shadow = this.$(".shadow"), - url = this._getCoverURL(); - - if (!this._isValidURL(url)) { - $shadow.hide(); - cdb.log.info("Header image url not valid"); - return; - } - - // configure spinner - var - target = document.getElementById('spinner'), - opts = { lines: 9, length: 4, width: 2, radius: 4, corners: 1, rotate: 0, color: '#ccc', speed: 1, trail: 60, shadow: true, hwaccel: false, zIndex: 2e9 }, - spinner = new Spinner(opts).spin(target); - - // create the image - var $img = $cover.find("img"); - - $img.hide(function() { - this.remove(); - }); - - $img = $("").attr("src", url); - $cover.append($img); - - $img.load(function(){ - spinner.stop(); - - var w = $img.width(); - var h = $img.height(); - var coverWidth = $cover.width(); - var coverHeight = $cover.height(); - - var ratio = h / w; - var coverRatio = coverHeight / coverWidth; - - // Resize rules - if ( w > coverWidth && h > coverHeight) { // bigger image - if ( ratio < coverRatio ) $img.css({ height: coverHeight }); - else { - var calculatedHeight = h / (w / coverWidth); - $img.css({ width: coverWidth, top: "50%", position: "absolute", "margin-top": -1*parseInt(calculatedHeight, 10)/2 }); - } - } else { - var calculatedHeight = h / (w / coverWidth); - $img.css({ width: coverWidth, top: "50%", position: "absolute", "margin-top": -1*parseInt(calculatedHeight, 10)/2 }); - } - - $img.fadeIn(300); - }) - .error(function(){ - spinner.stop(); - }); - }, - - /** - * Return true if the provided URL is valid - */ - _isValidURL: function(url) { - if (url) { - var urlPattern = /^(http|ftp|https):\/\/[\w-]+(\.[\w-]+)+([\w.,@?^=%&:\/~+#-|]*[\w@?^=%&\/~+#-])?$/ - return String(url).match(urlPattern) != null ? true : false; - } - - return false; - }, - - /** - * Toggle infowindow visibility - */ - toggle: function() { - this.model.get("visibility") ? this.show() : this.hide(); - }, - - /** - * Stop event bubbling - */ - _stopBubbling: function (e) { - e.preventDefault(); - e.stopPropagation(); - }, - - /** - * Stop event propagation - */ - _stopPropagation: function(ev) { - ev.stopPropagation(); - }, - - /** - * Set loading state adding its content - */ - setLoading: function() { - this.model.set({ - content: { - fields: [{ - title: null, - alternative_name: null, - value: 'Loading content...', - index: null, - type: "loading" - }], - data: {} - } - }) - return this; - }, - - /** - * Set loading state adding its content - */ - setError: function() { - this.model.set({ - content: { - fields: [{ - title: null, - alternative_name: null, - value: 'There has been an error...', - index: null, - type: 'error' - }], - data: {} - } - }) - return this; - }, - - /** - * Set the correct position for the popup - */ - setLatLng: function (latlng) { - this.model.set("latlng", latlng); - return this; - }, - - /** - * Close infowindow - */ - _closeInfowindow: function(ev) { - if (ev) { - ev.preventDefault(); - ev.stopPropagation(); - } - if (this.model.get("visibility")) { - this.model.set("visibility", false); - this.trigger('close'); - } - }, - - /** - * Set visibility infowindow - */ - showInfowindow: function() { - this.model.set("visibility", true); - }, - - /** - * Show infowindow (update, pan, etc) - */ - show: function (no_pan) { - var self = this; - - if (this.model.get("visibility")) { - self.$el.css({ left: -5000 }); - self._update(no_pan); - } - }, - - /** - * Get infowindow visibility - */ - isHidden: function () { - return !this.model.get("visibility"); - }, - - /** - * Set infowindow to hidden - */ - hide: function (force) { - if (force || !this.model.get("visibility")) this._animateOut(); - }, - - /** - * Update infowindow - */ - _update: function (no_pan) { - - if(!this.isHidden()) { - var delay = 0; - - if (!no_pan) { - var delay = this.adjustPan(); - } - - this._updatePosition(); - this._animateIn(delay); - } - }, - - /** - * Animate infowindow to show up - */ - _animateIn: function(delay) { - if (!$.browser.msie || ($.browser.msie && parseInt($.browser.version) > 8 )) { - this.$el.css({ - 'marginBottom':'-10px', - 'display':'block', - opacity:0 - }); - - this.$el - .delay(delay) - .animate({ - opacity: 1, - marginBottom: 0 - },300); - } else { - this.$el.show(); - } - }, - - /** - * Animate infowindow to disappear - */ - _animateOut: function() { - if (!$.browser.msie || ($.browser.msie && parseInt($.browser.version) > 8 )) { - var self = this; - this.$el.animate({ - marginBottom: "-10px", - opacity: "0", - display: "block" - }, 180, function() { - self.$el.css({display: "none"}); - }); - } else { - this.$el.hide(); - } - }, - - /** - * Update the position (private) - */ - _updatePosition: function () { - if(this.isHidden()) return; - - var - offset = this.model.get("offset") - pos = this.mapView.latLonToPixel(this.model.get("latlng")), - x = this.$el.position().left, - y = this.$el.position().top, - containerHeight = this.$el.outerHeight(true), - containerWidth = this.$el.width(), - left = pos.x - offset[0], - size = this.mapView.getSize(), - bottom = -1*(pos.y - offset[1] - size.y); - - this.$el.css({ bottom: bottom, left: left }); - }, - - /** - * Adjust pan to show correctly the infowindow - */ - adjustPan: function (callback) { - var offset = this.model.get("offset"); - - if (!this.model.get("autoPan") || this.isHidden()) { return; } - - var - x = this.$el.position().left, - y = this.$el.position().top, - containerHeight = this.$el.outerHeight(true) + 15, // Adding some more space - containerWidth = this.$el.width(), - pos = this.mapView.latLonToPixel(this.model.get("latlng")), - adjustOffset = {x: 0, y: 0}; - size = this.mapView.getSize() - wait_callback = 0; - - if (pos.x - offset[0] < 0) { - adjustOffset.x = pos.x - offset[0] - 10; - } - - if (pos.x - offset[0] + containerWidth > size.x) { - adjustOffset.x = pos.x + containerWidth - size.x - offset[0] + 10; - } - - if (pos.y - containerHeight < 0) { - adjustOffset.y = pos.y - containerHeight - 10; - } - - if (pos.y - containerHeight > size.y) { - adjustOffset.y = pos.y + containerHeight - size.y; - } - - if (adjustOffset.x || adjustOffset.y) { - this.mapView.panBy(adjustOffset); - wait_callback = 300; - } - - return wait_callback; - } - -}); - -cdb.geo.ui.Header = cdb.core.View.extend({ - - className: 'cartodb-header', - - initialize: function() { - var extra = this.model.get("extra"); - - this.model.set({ - title: extra.title, - description: this._setLinksTarget(extra.description), - show_title: extra.show_title, - show_description: extra.show_description - }, { silent: true }); - }, - - show: function() { - - //var display = this.model.get("display"); - var hasTitle = this.model.get("title") && this.model.get("show_title"); - var hasDescription = this.model.get("description") && this.model.get("show_description"); - - if (hasTitle || hasDescription) { - - var self = this; - - this.$el.show(); - - if (hasTitle) self.$title.show(); - if (hasDescription) self.$description.show(); - - } - - }, - - // Add target attribute to all links - _setLinksTarget: function(str) { - if (!str) return str; - var reg = new RegExp(/<(a)([^>]+)>/g); - return str.replace(reg, "<$1 target=\"_blank\"$2>"); - }, - - render: function() { - - this.$el.html(this.options.template(this.model.attributes)); - - this.$title = this.$el.find(".content div.title"); - this.$description = this.$el.find(".content div.description"); - - if (this.model.get("show_title") || this.model.get("show_description")) { - this.show(); - } else { - this.hide(); - } - - return this; - - } - -}); - -cdb.geo.ui.Search = cdb.core.View.extend({ - - className: 'cartodb-searchbox', - - events: { - "click input[type='text']": '_focus', - "submit form": '_submit', - "click": '_stopPropagation', - "dblclick": '_stopPropagation', - "mousedown": '_stopPropagation' - }, - - initialize: function() {}, - - render: function() { - this.$el.html(this.options.template(this.options)); - return this; - }, - - _stopPropagation: function(ev) { - ev.stopPropagation(); - }, - - _focus: function(ev) { - ev.preventDefault(); - - $(ev.target).focus(); - }, - - _showLoader: function() { - this.$('span.loader').show(); - }, - - _hideLoader: function() { - this.$('span.loader').hide(); - }, - - _submit: function(ev) { - ev.preventDefault(); - - var self = this - , address = this.$('input.text').val(); - - // Show geocoder loader - this._showLoader(); - - cdb.geo.geocoder.NOKIA.geocode(address, function(coords) { - if (coords.length>0) { - var validBBox = true; - - // check bounding box is valid - if(!coords[0].boundingbox || coords[0].boundingbox.south == coords[0].boundingbox.north || - coords[0].boundingbox.east == coords[0].boundingbox.west) { - validBBox = false; - } - - if (validBBox && coords[0].boundingbox) { - self.model.setBounds([ - [ - parseFloat(coords[0].boundingbox.south), - parseFloat(coords[0].boundingbox.west) - ], - [ - parseFloat(coords[0].boundingbox.north), - parseFloat(coords[0].boundingbox.east) - ] - ]); - } else if (coords[0].lat && coords[0].lon) { - self.model.setCenter([coords[0].lat, coords[0].lon]); - self.model.setZoom(10); - } - } - - // Hide geocoder loader - self._hideLoader(); - }); - } -}); - -/** - * Layer selector: it allows to select the layers that will be shown in the map - * - It needs the mapview, the element template and the dropdown template - * - * var layer_selector = new cdb.geo.ui.LayerSelector({ - * mapView: mapView, - * template: element_template, - * dropdown_template: dropdown_template - * }); - */ - -cdb.geo.ui.LayerSelector = cdb.core.View.extend({ - - className: 'cartodb-layer-selector-box', - - events: { - "click": '_openDropdown', - "dblclick": 'killEvent', - "mousedown": 'killEvent' - }, - - initialize: function() { - this.map = this.options.mapView.map; - - this.mapView = this.options.mapView; - this.mapView.bind('click zoomstart drag', function() { - this.dropdown && this.dropdown.hide() - }, this); - this.add_related_model(this.mapView); - - this.layers = []; - }, - - render: function() { - - this.$el.html(this.options.template(this.options)); - - this.dropdown = new cdb.ui.common.Dropdown({ - className:"cartodb-dropdown border", - template: this.options.dropdown_template, - target: this.$el.find("a"), - speedIn: 300, - speedOut: 200, - position: "position", - tick: "right", - vertical_position: "down", - horizontal_position: "right", - vertical_offset: 7, - horizontal_offset: 13 - }); - - if (cdb.god) cdb.god.bind("closeDialogs", this.dropdown.hide, this.dropdown); - - this.$el.append(this.dropdown.render().el); - - this._getLayers(); - this._setCount(); - - return this; - }, - - _getLayers: function() { - var self = this; - this.layers = []; - - _.each(this.map.layers.models, function(layer) { - - if (layer.get("type") == 'layergroup' || layer.get('type') === 'namedmap') { - var layerGroupView = self.mapView.getLayerByCid(layer.cid); - for (var i = 0 ; i < layerGroupView.getLayerCount(); ++i) { - var l = layerGroupView.getLayer(i); - var m = new cdb.core.Model(l); - m.set('order', i); - m.set('type', 'layergroup'); - - m.set('visible', !layerGroupView.getSubLayer(i).get('hidden')); - m.bind('change:visible', function(model) { - this.trigger("change:visible", model.get('visible'), model.get('order'), model); - }, self); - - if(self.options.layer_names) { - m.set('layer_name', self.options.layer_names[i]); - } else { - m.set('layer_name', l.options.layer_name); - } - - var layerView = self._createLayer('LayerViewFromLayerGroup', { - model: m, - layerView: layerGroupView, - layerIndex: i - }); - layerView.bind('switchChanged', self._setCount, self); - self.layers.push(layerView); - } - } else if (layer.get("type") === "CartoDB" || layer.get('type') === 'torque') { - var layerView = self._createLayer('LayerView', { model: layer }); - layerView.bind('switchChanged', self._setCount, self); - self.layers.push(layerView); - layerView.model.bind('change:visible', function(model) { - this.trigger("change:visible", model.get('visible'), model.get('order'), model); - }, self); - } - - }); - }, - - _createLayer: function(_class, opts) { - var layerView = new cdb.geo.ui[_class](opts); - this.$("ul").append(layerView.render().el); - this.addView(layerView); - return layerView; - }, - - _setCount: function() { - var count = 0; - for (var i = 0, l = this.layers.length; i < l; ++i) { - var lyr = this.layers[i]; - - if (lyr.model.get('visible')) { - count++; - } - } - - this.$('.count').text(count); - this.trigger("switchChanged", this); - }, - - _openDropdown: function() { - this.dropdown.open(); - } - -}); - - - - - - -/** - * View for each CartoDB layer - * - It needs a model to make it work. - * - * var layerView = new cdb.geo.ui.LayerView({ - * model: layer_model, - * layer_definition: layer_definition - * }); - * - */ - -cdb.geo.ui.LayerView = cdb.core.View.extend({ - - tagName: "li", - - defaults: { - template: '\ - <%= layer_name %>\ - switch">\ - ' - }, - - events: { - "click": '_onSwitchClick' - }, - - initialize: function() { - - if (!this.model.has('visible')) this.model.set('visible', false); - - this.model.bind("change:visible", this._onSwitchSelected, this); - - this.add_related_model(this.model); - - this._onSwitchSelected(); - - // Template - this.template = this.options.template ? cdb.templates.getTemplate(this.options.template) : _.template(this.defaults.template); - }, - - render: function() { - var attrs = _.clone(this.model.attributes); - attrs.layer_name = attrs.layer_name || attrs.table_name; - this.$el.append(this.template(attrs)); - return this; - }, - - /* - * Throw an event when the user clicks in the switch button - */ - _onSwitchSelected: function() { - var enabled = this.model.get('visible'); - - // Change switch - this.$el.find(".switch") - .removeClass(enabled ? 'disabled' : 'enabled') - .addClass(enabled ? 'enabled' : 'disabled'); - - // Send trigger - this.trigger('switchChanged'); - - }, - - _onSwitchClick: function(e){ - this.killEvent(e); - - // Set model - this.model.set("visible", !this.model.get("visible")); - } - -}); - -/** - * View for each layer from a layer group - * - It needs a model and the layer_definition to make it work. - * - * var layerView = new cdb.geo.ui.LayerViewFromLayerGroup({ - * model: layer_model, - * layerView: layweView - * }); - * - */ - -cdb.geo.ui.LayerViewFromLayerGroup = cdb.geo.ui.LayerView.extend({ - - _onSwitchSelected: function() { - - cdb.geo.ui.LayerView.prototype._onSwitchSelected.call(this); - var sublayer = this.options.layerView.getSubLayer(this.options.layerIndex) - var visible = this.model.get('visible'); - - if (visible) { - sublayer.show(); - } else { - sublayer.hide(); - } - } -}); -cdb.geo.ui.MobileLayer = cdb.core.View.extend({ - - events: { - 'click h3': "_toggle", - "dblclick": "_stopPropagation" - }, - - tagName: "li", - - className: "cartodb-mobile-layer has-toggle", - - template: cdb.core.Template.compile("<% if (show_title) { %>

        <%= layer_name %><% } %>

        "), - - /** - * Stop event propagation - */ - _stopPropagation: function(ev) { - ev.stopPropagation(); - }, - - initialize: function() { - - _.defaults(this.options, this.default_options); - - this.model.bind("change:visible", this._onChangeVisible, this); - - }, - - _onChangeVisible: function() { - - this.$el.find(".legend")[ this.model.get("visible") ? "fadeIn":"fadeOut"](150); - this.$el[ this.model.get("visible") ? "removeClass":"addClass"]("hidden"); - - this.trigger("change_visibility", this); - - }, - - _toggle: function(e) { - - e.preventDefault(); - e.stopPropagation(); - - if (this.options.hide_toggle) return; - - this.model.set("visible", !this.model.get("visible")) - - }, - - _renderLegend: function() { - - if (!this.options.show_legends) return; - - if (this.model.get("legend") && (this.model.get("legend").type == "none" || !this.model.get("legend").type)) return; - if (this.model.get("legend") && this.model.get("legend").items && this.model.get("legend").items.length == 0) return; - - this.$el.addClass("has-legend"); - - var legend = new cdb.geo.ui.Legend(this.model.get("legend")); - - legend.undelegateEvents(); - - this.$el.append(legend.render().$el); - - }, - - _truncate: function(input, length) { - return input.substr(0, length-1) + (input.length > length ? '…' : ''); - }, - - render: function() { - - var layer_name = this.model.get("layer_name"); - - layer_name = layer_name ? this._truncate(layer_name, 23) : "untitled"; - - var attributes = _.extend( - this.model.attributes, - { - layer_name: this.options.show_title ? layer_name : "", - toggle_class: this.options.hide_toggle ? " hide" : "" - } - ); - - this.$el.html(this.template(_.extend(attributes, { show_title: this.options.show_title } ))); - - - if (this.options.hide_toggle) this.$el.removeClass("has-toggle"); - if (!this.model.get("visible")) this.$el.addClass("hidden"); - if (this.model.get("legend")) this._renderLegend(); - - this._onChangeVisible(); - - return this; - } - -}); - -cdb.geo.ui.Mobile = cdb.core.View.extend({ - - className: "cartodb-mobile", - - events: { - "click .cartodb-attribution-button": "_onAttributionClick", - "click .toggle": "_toggle", - "click .fullscreen": "_toggleFullScreen", - "click .backdrop": "_onBackdropClick", - "dblclick .aside": "_stopPropagation", - "dragstart .aside": "_checkOrigin", - "mousedown .aside": "_checkOrigin", - "touchstart .aside": "_checkOrigin", - "MSPointerDown .aside": "_checkOrigin", - }, - - initialize: function() { - - _.bindAll(this, "_toggle", "_reInitScrollpane"); - - _.defaults(this.options, this.default_options); - - this.hasLayerSelector = false; - this.mobileEnabled = /Android|webOS|iPhone|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent); - - this.visibility_options = this.options.visibility_options || {}; - - this.mapView = this.options.mapView; - this.map = this.mapView.map; - - this.template = this.options.template ? this.options.template : cdb.templates.getTemplate('geo/zoom'); - this.overlays = this.options.overlays; - - this._setupModel(); - - window.addEventListener('orientationchange', _.bind(this.doOnOrientationChange, this)); - - this._addWheelEvent(); - - }, - - _addWheelEvent: function() { - - var self = this; - var mapView = this.options.mapView; - - $(document).on('webkitfullscreenchange mozfullscreenchange fullscreenchange', function() { - - if ( !document.fullscreenElement && !document.webkitFullscreenElement && !document.mozFullScreenElement && !document.msFullscreenElement) { - mapView.options.map.set("scrollwheel", false); - } - - mapView.invalidateSize(); - - }); - - }, - - _setupModel: function() { - - this.model = new Backbone.Model({ - open: false, - layer_count: 0 - }); - - this.model.on("change:open", this._onChangeOpen, this); - this.model.on("change:layer_count", this._onChangeLayerCount, this); - - }, - - /** - * Check event origin - */ - _checkOrigin: function(ev) { - // If the mouse down come from jspVerticalBar - // dont stop the propagation, but if the event - // is a touchstart, stop the propagation - var come_from_scroll = (($(ev.target).closest(".jspVerticalBar").length > 0) && (ev.type != "touchstart")); - - if (!come_from_scroll) { - ev.stopPropagation(); - } - }, - - _stopPropagation: function(ev) { - ev.stopPropagation(); - }, - - _onBackdropClick: function(e) { - - e.preventDefault(); - e.stopPropagation(); - - this.$el.find(".backdrop").fadeOut(250); - this.$el.find(".cartodb-attribution").fadeOut(250); - - }, - - _onAttributionClick: function(e) { - - e.preventDefault(); - e.stopPropagation(); - - this.$el.find(".backdrop").fadeIn(250); - this.$el.find(".cartodb-attribution").fadeIn(250); - - }, - - _toggle: function(e) { - - e.preventDefault(); - e.stopPropagation(); - - this.model.set("open", !this.model.get("open")); - - }, - - _toggleFullScreen: function(ev) { - - ev.stopPropagation(); - ev.preventDefault(); - - var doc = window.document; - var docEl = $("#map > div")[0]; - - var requestFullScreen = docEl.requestFullscreen || docEl.mozRequestFullScreen || docEl.webkitRequestFullScreen; - var cancelFullScreen = doc.exitFullscreen || doc.mozCancelFullScreen || doc.webkitExitFullscreen; - - var mapView = this.options.mapView; - - if (!doc.fullscreenElement && !doc.mozFullScreenElement && !doc.webkitFullscreenElement) { - - requestFullScreen.call(docEl); - - if (mapView) { - - mapView.options.map.set("scrollwheel", true); - - } - - } else { - - cancelFullScreen.call(doc); - - } - }, - - _open: function() { - - var right = this.$el.find(".aside").width(); - - this.$el.find(".cartodb-header").animate({ right: right }, 200) - this.$el.find(".aside").animate({ right: 0 }, 200) - this.$el.find(".cartodb-attribution-button").animate({ right: right + parseInt(this.$el.find(".cartodb-attribution-button").css("right")) }, 200) - this.$el.find(".cartodb-attribution").animate({ right: right + parseInt(this.$el.find(".cartodb-attribution-button").css("right")) }, 200) - this._initScrollPane(); - - }, - - _close: function() { - - this.$el.find(".cartodb-header").animate({ right: 0 }, 200) - this.$el.find(".aside").animate({ right: - this.$el.find(".aside").width() }, 200) - this.$el.find(".cartodb-attribution-button").animate({ right: 20 }, 200) - this.$el.find(".cartodb-attribution").animate({ right: 20 }, 200) - - }, - - default_options: { - timeout: 0, - msg: '' - }, - - _stopPropagation: function(ev) { - ev.stopPropagation(); - }, - - doOnOrientationChange: function() { - - switch(window.orientation) - { - case -90: - case 90: this.recalc("landscape"); - break; - default: this.recalc("portrait"); - break; - } - }, - - recalc: function(orientation) { - - var height = $(".legends > div.cartodb-legend-stack").height(); - - if (this.$el.hasClass("open") && height < 100 && !this.$el.hasClass("torque")) { - - this.$el.css("height", height); - this.$el.find(".top-shadow").hide(); - this.$el.find(".bottom-shadow").hide(); - - } else if (this.$el.hasClass("open") && height < 100 && this.$el.hasClass("legends") && this.$el.hasClass("torque")) { - - this.$el.css("height", height + $(".legends > div.torque").height() ); - this.$el.find(".top-shadow").hide(); - this.$el.find(".bottom-shadow").hide(); - - } - - }, - - _onChangeLayerCount: function() { - - var layer_count = this.model.get("layer_count"); - var msg = layer_count + " layer" + (layer_count != 1 ? "s" : ""); - this.$el.find(".aside .layer-container > h3").html(msg); - - }, - - _onChangeOpen: function() { - this.model.get("open") ? this._open() : this._close(); - }, - - _createLayer: function(_class, opts) { - return new cdb.geo.ui[_class](opts); - }, - - _getLayers: function() { - - this.layers = []; - - // we add the layers to the array depending on the method used - // to sent us the layers - if (this.options.layerView) { - this._getLayersFromLayerView(); - } else { - _.each(this.map.layers.models, this._getLayer, this); - } - - }, - - _getLayersFromLayerView: function() { - - if (this.options.layerView && this.options.layerView.model.get("type") == "layergroup") { - - this.layers = _.map(this.options.layerView.layers, function(l, i) { - - var m = new cdb.core.Model(l); - - m.set('order', i); - m.set('type', 'layergroup'); - m.set('visible', l.visible); - m.set('layer_name', l.options.layer_name); - - layerView = this._createLayer('LayerViewFromLayerGroup', { - model: m, - layerView: this.options.layerView, - layerIndex: i - }); - - return layerView.model; - - }, this); - - } else if (this.options.layerView && (this.options.layerView.model.get("type") == "torque")) { - - var layerView = this._createLayer('LayerView', { model: this.options.layerView.model }); - - this.layers.push(layerView.model); - - } - }, - - _getLayer: function(layer) { - - if (layer.get("type") == 'layergroup' || layer.get('type') === 'namedmap') { - - var layerGroupView = this.mapView.getLayerByCid(layer.cid); - - for (var i = 0 ; i < layerGroupView.getLayerCount(); ++i) { - - var l = layerGroupView.getLayer(i); - var m = new cdb.core.Model(l); - - m.set('order', i); - m.set('type', 'layergroup'); - m.set('visible', l.visible); - m.set('layer_name', l.options.layer_name); - - layerView = this._createLayer('LayerViewFromLayerGroup', { - model: m, - layerView: layerGroupView, - layerIndex: i - }); - - this.layers.push(layerView.model); - - } - - } else if (layer.get("type") === "CartoDB" || layer.get('type') === 'torque') { - - if (layer.get('type') === 'torque') { - layer.on("change:visible", this._toggleSlider, this); - } - - this.layers.push(layer); - - } - - }, - - _toggleSlider: function(m) { - - if (m.get("visible")) { - this.$el.addClass("with-torque"); - this.slider.show(); - } else { - this.$el.removeClass("with-torque"); - this.slider.hide(); - } - - }, - - _reInitScrollpane: function() { - this.$('.scrollpane').data('jsp') && this.$('.scrollpane').data('jsp').reinitialise(); - }, - - _bindOrientationChange: function() { - - var self = this; - - var onOrientationChange = function() { - $(".cartodb-mobile .scrollpane").css("max-height", self.$el.height() - 30); - $('.cartodb-mobile .scrollpane').data('jsp') && $('.cartodb-mobile .scrollpane').data('jsp').reinitialise(); - }; - - if (!window.addEventListener) { - window.attachEvent('orientationchange', onOrientationChange, this); - } else { - window.addEventListener('orientationchange', _.bind(onOrientationChange)); - } - - }, - - _renderOverlays: function() { - - var hasSearchOverlay = false; - var hasZoomOverlay = false; - var hasLayerSelector = false; - - _.each(this.overlays, function(overlay) { - - if (!this.visibility_options.search && overlay.type == 'search') { - if (this.visibility_options.search !== false && this.visibility_options.search !== "false") { - this._addSearch(); - hasSearchOverlay = true; - } - } - - if (!this.visibility_options.zoomControl && overlay.type == 'zoom') { - if (this.visibility_options.zoomControl !== "false") { - this._addZoom(); - hasZoomOverlay = true; - } - } - - if (overlay.type == 'fullscreen' && !this.mobileEnabled) { - this._addFullscreen(); - } - - if (overlay.type == 'header') { - this._addHeader(overlay); - } - - if (overlay.type == 'layer_selector') { - hasLayerSelector = true; - } - - }, this); - - var search_visibility = this.visibility_options.search === "true" || this.visibility_options.search === true; - var zoom_visibility = this.visibility_options.zoomControl === "true" || this.visibility_options.zoomControl === true; - var layer_selector_visibility = this.visibility_options.layer_selector; - - if (!hasSearchOverlay && search_visibility) this._addSearch(); - if (!hasZoomOverlay && zoom_visibility) this._addZoom(); - if (layer_selector_visibility || hasLayerSelector && layer_selector_visibility == undefined) this.hasLayerSelector = true; - - }, - - _initScrollPane: function() { - - if (this.$scrollpane) return; - - var self = this; - - var height = this.$el.height(); - this.$scrollpane = this.$el.find(".scrollpane"); - - setTimeout(function() { - self.$scrollpane.css("max-height", height - 60); - self.$scrollpane.jScrollPane({ showArrows: true }); - }, 500); - - }, - - _addZoom: function() { - - var template = cdb.core.Template.compile('\ - +\ - -\ -
        ', 'mustache' - ); - - var zoom = new cdb.geo.ui.Zoom({ - model: this.options.map, - template: template - }); - - this.$el.append(zoom.render().$el); - this.$el.addClass("with-zoom"); - - }, - - _addFullscreen: function() { - - if (this.visibility_options.fullscreen != false) { - this.hasFullscreen = true; - this.$el.addClass("with-fullscreen"); - } - - }, - - _addSearch: function() { - - this.hasSearch = true; - - var template = cdb.core.Template.compile('\ -
        \ - \ - \ - \ - \ - ', 'mustache' - ); - - var search = new cdb.geo.ui.Search({ - template: template, - model: this.mapView.map - }); - - this.$el.find(".aside").prepend(search.render().$el); - this.$el.find(".cartodb-searchbox").show(); - this.$el.addClass("with-search"); - - }, - - _addHeader: function(overlay) { - - this.hasHeader = true; - - this.$header = this.$el.find(".cartodb-header"); - - var title_template = _.template('
        <% if (show_title) { %>
        <%= title %>
        <% } %><% if (show_description) { %>
        <%= description %><% } %>
        '); - - var extra = overlay.options.extra; - var has_header = false; - var show_title = false, show_description = false; - - if (extra) { - - if (this.visibility_options.title || this.visibility_options.title != false && extra.show_title) { - has_header = true; - show_title = true; - } - - if (this.visibility_options.description || this.visibility_options.description != false && extra.show_description) { - has_header = true; - show_description = true; - } - - var $hgroup = title_template({ - title: extra.title, - show_title:show_title, - description: extra.description, - show_description: show_description - }); - - if (has_header) { - this.$el.addClass("with-header"); - this.$header.find(".content").append($hgroup); - } - - } - - }, - - _addAttributions: function() { - - var attributions = ""; - - this.options.mapView.$el.find(".leaflet-control-attribution").hide(); // TODO: remove this from here - - if (this.options.layerView) { - - attributions = this.options.layerView.model.get("attribution"); - this.$el.find(".cartodb-attribution").append(attributions); - - } else if (this.options.map.get("attribution")) { - - attributions = this.options.map.get("attribution"); - - _.each(attributions, function(attribution) { - var $li = $("
      • "); - var $el = $li.html(attribution); - this.$el.find(".cartodb-attribution").append($li); - }, this); - - } - - if (attributions) { - this.$el.find(".cartodb-attribution-button").fadeIn(250); - } - - }, - - _renderLayers: function() { - - var hasLegendOverlay = this.visibility_options.legends; - - var legends = this.layers.filter(function(layer) { - return layer.get("legend") && layer.get("legend").type !== "none" - }); - - var hasLegends = legends.length ? true : false; - - if (!this.hasLayerSelector && !hasLegendOverlay) return; - if (!this.hasLayerSelector && !hasLegends) return; - if (this.layers.length == 0) return; - if (this.layers.length == 1 && !hasLegends) return; - - this.$el.addClass("with-layers"); - - this.model.set("layer_count", 0); - - if (!this.hasSearch) this.$el.find(".aside .layer-container").prepend("

        "); - - _.each(this.layers, this._renderLayer, this); - - }, - - _renderLayer: function(data) { - - var hasLegend = data.get("legend") && data.get("legend").type !== "" && data.get("legend").type !== "none"; - - // When the layer selector is disabled, don't show the layer if it doesn't have legends - if (!this.hasLayerSelector && !hasLegend) return; - if (!this.hasLayerSelector && !data.get("visible")) return; - - var hide_toggle = (this.layers.length == 1 || !this.hasLayerSelector); - - var show_legends = true; - - if (this.visibility_options && this.visibility_options.legends !== undefined) { - show_legends = this.visibility_options.legends; - } - - var layer = new cdb.geo.ui.MobileLayer({ - model: data, - show_legends: show_legends, - show_title: !this.hasLayerSelector ? false : true, - hide_toggle: hide_toggle - }); - - this.$el.find(".aside .layers").append(layer.render().$el); - - layer.bind("change_visibility", this._reInitScrollpane, this); - - this.model.set("layer_count", this.model.get("layer_count") + 1); - - }, - - _renderTorque: function() { - - if (this.options.torqueLayer) { - - this.hasTorque = true; - - this.slider = new cdb.geo.ui.TimeSlider({type: "time_slider", layer: this.options.torqueLayer, map: this.options.map, pos_margin: 0, position: "none" , width: "auto" }); - - this.slider.bind("time_clicked", function() { - this.slider.toggleTime(); - }, this); - - this.$el.find(".torque").append(this.slider.render().$el); - - if (this.options.torqueLayer.hidden) this.slider.hide(); - else this.$el.addClass("with-torque"); - } - - }, - - render:function() { - - this._bindOrientationChange(); - - this.$el.html(this.template(this.options)); - - this._renderOverlays(); - - this._addAttributions(); - - this.$header = this.$el.find(".cartodb-header"); - this.$header.show(); - - this._getLayers(); - this._renderLayers(); - this._renderTorque(); - - return this; - - } - -}); -/** - * Show or hide tiles loader - * - * Usage: - * - * var tiles_loader = new cdb.geo.ui.TilesLoader(); - * mapWrapper.$el.append(tiles_loader.render().$el); - * - */ - - -cdb.geo.ui.TilesLoader = cdb.core.View.extend({ - - className: "cartodb-tiles-loader", - - default_options: { - animationSpeed: 500 - }, - - initialize: function() { - _.defaults(this.options, this.default_options); - this.isVisible = 0; - this.template = this.options.template ? this.options.template : cdb.templates.getTemplate('geo/tiles_loader'); - }, - - render: function() { - this.$el.html($(this.template(this.options))); - return this; - }, - - show: function(ev) { - if(this.isVisible) return; - if (!$.browser.msie || ($.browser.msie && $.browser.version.indexOf("9.") != 0)) { - this.$el.fadeTo(this.options.animationSpeed, 1) - } else { - this.$el.show(); - } - this.isVisible++; - }, - - hide: function(ev) { - this.isVisible--; - if(this.isVisible > 0) return; - this.isVisible = 0; - if (!$.browser.msie || ($.browser.msie && $.browser.version.indexOf("9.") == 0)) { - this.$el.stop(true).fadeTo(this.options.animationSpeed, 0) - } else { - this.$el.hide(); - } - }, - - visible: function() { - return this.isVisible > 0; - } - -}); - -cdb.geo.ui.InfoBox = cdb.core.View.extend({ - - className: 'cartodb-infobox', - defaults: { - pos_margin: 20, - position: 'bottom|right', - width: 200 - }, - - initialize: function() { - var self = this; - _.defaults(this.options, this.defaults); - if(this.options.layer) { - this.enable(); - } - this.setTemplate(this.options.template || this.defaultTemplate, 'mustache'); - }, - - setTemplate: function(tmpl) { - this.template = cdb.core.Template.compile(tmpl, 'mustache'); - }, - - enable: function() { - if(this.options.layer) { - this.options.layer - .on('featureOver', function(e, latlng, pos, data) { - this.render(data).show(); - }, this) - .on('featureOut', function() { - this.hide(); - }, this); - } - }, - - disable: function() { - if(this.options.layer) { - this.options.layer.off(null, null, this); - } - }, - - // set position based on a string like "top|right", "top|left", "bottom|righ"... - setPosition: function(pos) { - var props = {}; - if(pos.indexOf('top') !== -1) { - props.top = this.options.pos_margin; - } else if(pos.indexOf('bottom') !== -1) { - props.bottom = this.options.pos_margin; - } - - if(pos.indexOf('left') !== -1) { - props.left = this.options.pos_margin; - } else if(pos.indexOf('right') !== -1) { - props.right = this.options.pos_margin; - } - this.$el.css(props); - - }, - - render: function(data) { - this.$el.html( this.template(data) ); - if(this.options.width) { - this.$el.css('width', this.options.width); - } - if(this.options.position) { - this.setPosition(this.options.position); - } - return this; - } - -}); - - -cdb.geo.ui.Tooltip = cdb.geo.ui.InfoBox.extend({ - - defaultTemplate: '

        {{text}}

        ', - className: 'cartodb-tooltip', - - defaults: { - vertical_offset: 0, - horizontal_offset: 0, - position: 'top|center' - }, - - initialize: function() { - this.options.template = this.options.template || this.defaultTemplate; - cdb.geo.ui.InfoBox.prototype.initialize.call(this); - this._filter = null; - this.showing = false; - this.showhideTimeout = null; - }, - - setLayer: function(layer) { - this.options.layer = layer; - return this; - }, - - /** - * sets a filter to open the tooltip. If the feature being hovered - * pass the filter the tooltip is shown - * setFilter(null) removes the filter - */ - setFilter: function(f) { - this._filter = f; - return this; - }, - - setFields: function(fields) { - this.options.fields = fields; - return this; - }, - - setAlternativeNames: function(n) { - this.options.alternative_names = n; - }, - - enable: function() { - if(this.options.layer) { - // unbind previous events - this.options.layer.unbind(null, null, this); - this.options.layer - .on('mouseover', function(e, latlng, pos, data) { - // this flag is used to be compatible with previous templates - // where the data is not enclosed a content variable - if (this.options.fields) { - - var non_valid_keys = ['fields', 'content']; - - if (this.options.omit_columns) { - non_valid_keys = non_valid_keys.concat(this.options.omit_columns); - } - - var c = cdb.geo.ui.InfowindowModel.contentForFields(data, this.options.fields, { - empty_fields: this.options.empty_fields - }); - // Remove fields and content from data - // and make them visible for custom templates - data.content = _.omit(data, non_valid_keys); - - // loop through content values - data.fields = c.fields; - - // alternamte names - var names = this.options.alternative_names; - if (names) { - for(var i = 0; i < data.fields.length; ++i) { - var f = data.fields[i]; - f.title = names[f.title] || f.title; - } - } - } - this.show(pos, data); - this.showing = true; - }, this) - .on('mouseout', function() { - if (this.showing) { - this.hide(); - this.showing = false; - } - }, this); - this.add_related_model(this.options.layer); - } - }, - - disable: function() { - if(this.options.layer) { - this.options.layer.unbind(null, null, this); - } - this.hide(); - this.showing = false; - }, - - _visibility: function() { - var self = this; - clearTimeout(this.showhideTimeout); - this.showhideTimeout = setTimeout(self._showing ? - function() { self.$el.fadeIn(100); } - : - function() { self.$el.fadeOut(200); } - , 50); - }, - - hide: function() { - if (this._showing) { - this._showing = false; - this._visibility(); - } - }, - - show: function(pos, data) { - if (this._filter && !this._filter(data)) { - return this; - } - this.render(data); - //this.elder('show', pos, data); - this.setPosition(pos); - if (!this._showing) { - this._showing = true; - this._visibility(); - } - return this; - }, - - setPosition: function(point) { - var props = { - left: 0, - top: 0 - }; - - var pos = this.options.position; - var $el = this.$el; - var h = $el.innerHeight(); - var w = $el.innerWidth(); - - // Vertically - if (pos.indexOf('top') !== -1) { - props.top = -h; - } else if (pos.indexOf('middle') !== -1) { - props.top = -(h/2); - } - - // Horizontally - if(pos.indexOf('left') !== -1) { - props.left = -w; - } else if(pos.indexOf('center') !== -1) { - props.left = -(w/2); - } - - // Offsets - props.top += this.options.vertical_offset; - props.left += this.options.horizontal_offset; - - $el.css({ - top: (point.y + props.top), - left: (point.x + props.left) - }); - - }, - - render: function(data) { - this.$el.html( this.template(data) ); - return this; - } - -}); - -/** - * FullScreen widget: - * - * var widget = new cdb.ui.common.FullScreen({ - * doc: ".container", // optional; if not specified, we do the fullscreen of the whole window - * template: this.getTemplate("table/views/fullscreen") - * }); - * - */ - -cdb.ui.common.FullScreen = cdb.core.View.extend({ - - tagName: 'div', - className: 'cartodb-fullscreen', - - events: { - - "click a": "_toggleFullScreen" - - }, - - initialize: function() { - - _.bindAll(this, 'render'); - _.defaults(this.options, this.default_options); - - //this.model = new cdb.core.Model({ - //allowWheelOnFullscreen: false - //}); - - this._addWheelEvent(); - - }, - - _addWheelEvent: function() { - - var self = this; - var mapView = this.options.mapView; - - $(document).on('webkitfullscreenchange mozfullscreenchange fullscreenchange', function() { - - if ( !document.fullscreenElement && !document.webkitFullscreenElement && !document.mozFullScreenElement && !document.msFullscreenElement) { - if (self.model.get("allowWheelOnFullscreen")) { - mapView.options.map.set("scrollwheel", false); - } - } - - mapView.invalidateSize(); - - }); - - }, - - _toggleFullScreen: function(ev) { - - ev.stopPropagation(); - ev.preventDefault(); - - var doc = window.document; - var docEl = doc.documentElement; - - if (this.options.doc) { // we use a custom element - docEl = $(this.options.doc)[0]; - } - - var requestFullScreen = docEl.requestFullscreen || docEl.mozRequestFullScreen || docEl.webkitRequestFullScreen || docEl.msRequestFullscreen; - var cancelFullScreen = doc.exitFullscreen || doc.mozCancelFullScreen || doc.webkitExitFullscreen || doc.msExitFullscreen; - - var mapView = this.options.mapView; - - if (!doc.fullscreenElement && !doc.mozFullScreenElement && !doc.webkitFullscreenElement && !doc.msFullscreenElement) { - - requestFullScreen.call(docEl); - - if (mapView) { - - if (this.model.get("allowWheelOnFullscreen")) { - mapView.options.map.set("scrollwheel", true); - } - - } - - } else { - - cancelFullScreen.call(doc); - - } - }, - - render: function() { - - var $el = this.$el; - - var options = _.extend(this.options); - - $el.html(this.options.template(options)); - - return this; - } - -}); - - -function Map(options) { - var self = this; - this.options = _.defaults(options, { - ajax: window.$ ? window.$.ajax : reqwest.compat, - pngParams: ['map_key', 'api_key', 'cache_policy', 'updated_at'], - gridParams: ['map_key', 'api_key', 'cache_policy', 'updated_at'], - cors: this.isCORSSupported(), - btoa: this.isBtoaSupported() ? this._encodeBase64Native : this._encodeBase64, - MAX_GET_SIZE: 2033, - force_cors: false, - instanciateCallback: function() { - return '_cdbc_' + self._callbackName(); - } - }); - - this.layerToken = null; - this.urls = null; - this.silent = false; - this.interactionEnabled = []; //TODO: refactor, include inside layer - this._layerTokenQueue = []; - this._timeout = -1; - this._queue = []; - this._waiting = false; - this.lastTimeUpdated = null; - this._refreshTimer = -1; -} - -Map.BASE_URL = '/api/v1/map'; -Map.EMPTY_GIF = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7"; - -function NamedMap(named_map, options) { - var self = this; - Map.call(this, options); - this.options.pngParams.push('auth_token') - this.options.gridParams.push('auth_token') - this.endPoint = Map.BASE_URL + '/named/' + named_map.name; - this.JSONPendPoint = Map.BASE_URL + '/named/' + named_map.name + '/jsonp'; - this.layers = _.clone(named_map.layers) || []; - for(var i = 0; i < this.layers.length; ++i) { - var layer = this.layers[i]; - layer.options = layer.options || { hidden: false }; - layer.options.layer_name = layer.layer_name; - } - this.named_map = named_map; - this.stat_tag = named_map.stat_tag; - var token = named_map.auth_token || options.auth_token; - if (token) { - this.setAuthToken(token); - } -} - -function LayerDefinition(layerDefinition, options) { - var self = this; - Map.call(this, options); - this.endPoint = Map.BASE_URL; - this.setLayerDefinition(layerDefinition, { silent: true }); -} - -/** - * given a list of sublayers as: - * { - * sql: '...', - * cartocss: '..', - * cartocss_version:'...', //optional - * interactivity: '...' //optional - * } - * returns the layer definition for version 1.0.0 - * - * ``sublayers`` should be an array, an exception is thrown otherewise - * - */ -LayerDefinition.layerDefFromSubLayers = function(sublayers) { - - if(!sublayers || sublayers.length === undefined) throw new Error("sublayers should be an array"); - - var layer_definition = { - version: '1.0.0', - stat_tag: 'API', - layers: [] - }; - - for (var i = 0; i < sublayers.length; ++i) { - layer_definition.layers.push({ - type: 'cartodb', - options: sublayers[i] - }); - } - - return layer_definition; -}; - -Map.prototype = { - - /* - * TODO: extract these two functions to some core module - */ - isCORSSupported: function() { - return 'withCredentials' in new XMLHttpRequest();// || (typeof XDomainRequest !== "undefined"; - }, - - isBtoaSupported: function() { - return typeof window['btoa'] == 'function'; - }, - - getLayerCount: function() { - return this.layers.length; - }, - - _encodeBase64Native: function (input) { - return btoa(input) - }, - - _callbackName: function() { - return cartodb.uniqueCallbackName(JSON.stringify(this.toJSON())); - }, - - // given number inside layergroup - // returns the real index in tiler layergroup` - getLayerIndexByNumber: function(number) { - var layers = {} - var c = 0; - for(var i = 0; i < this.layers.length; ++i) { - var layer = this.layers[i]; - layers[i] = c; - if(layer.options && !layer.options.hidden) { - ++c; - } - } - return layers[number]; - }, - - /** - * return the layer number by index taking into - * account the hidden layers. - */ - getLayerNumberByIndex: function(index) { - var layers = []; - for(var i = 0; i < this.layers.length; ++i) { - var layer = this.layers[i]; - if(layer.options && !layer.options.hidden) { - layers.push(i); - } - } - if (index >= layers.length) { - return -1; - } - return +layers[index]; - }, - - visibleLayers: function() { - var layers = []; - for(var i = 0; i < this.layers.length; ++i) { - var layer = this.layers[i]; - if(!layer.options.hidden) { - layers.push(layer); - } - } - return layers; - }, - - - // ie7 btoa, - // from http://phpjs.org/functions/base64_encode/ - _encodeBase64: function (data) { - var b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; - var o1, o2, o3, h1, h2, h3, h4, bits, i = 0, - ac = 0, - enc = "", - tmp_arr = []; - - if (!data) { - return data; - } - - do { // pack three octets into four hexets - o1 = data.charCodeAt(i++); - o2 = data.charCodeAt(i++); - o3 = data.charCodeAt(i++); - - bits = o1 << 16 | o2 << 8 | o3; - - h1 = bits >> 18 & 0x3f; - h2 = bits >> 12 & 0x3f; - h3 = bits >> 6 & 0x3f; - h4 = bits & 0x3f; - - // use hexets to index into b64, and append result to encoded string - tmp_arr[ac++] = b64.charAt(h1) + b64.charAt(h2) + b64.charAt(h3) + b64.charAt(h4); - } while (i < data.length); - - enc = tmp_arr.join(''); - - var r = data.length % 3; - return (r ? enc.slice(0, r - 3) : enc) + '==='.slice(r || 3); - }, - - _array2hex: function(byteArr) { - var encoded = [] - for(var i = 0; i < byteArr.length; ++i) { - encoded.push(String.fromCharCode(byteArr[i] + 128)); - } - return this.options.btoa(encoded.join('')) - }, - - getLayerToken: function(callback) { - var self = this; - function _done(data, err) { - var fn; - while(fn = self._layerTokenQueue.pop()) { - fn(data, err); - } - } - clearTimeout(this._timeout); - this._queue.push(_done); - this._layerTokenQueue.push(callback); - this._timeout = setTimeout(function() { - self._getLayerToken(_done); - }, 4); - }, - - _requestFinished: function() { - var self = this; - this._waiting = false; - this.lastTimeUpdated = new Date().getTime(); - - // refresh layer when invalidation time has passed - clearTimeout(this._refreshTimer); - this._refreshTimer = setTimeout(function() { - self.invalidate(); - }, this.options.refreshTime || (60*120*1000)); // default layergroup ttl - - // check request queue - if(this._queue.length) { - var last = this._queue[this._queue.length - 1]; - this._getLayerToken(last); - } - }, - - _requestPOST: function(params, callback) { - var self = this; - var ajax = this.options.ajax; - - var loadingTime = cartodb.core.Profiler.metric('cartodb-js.layergroup.post.time').start(); - - ajax({ - crossOrigin: true, - type: 'POST', - method: 'POST', - dataType: 'json', - contentType: 'application/json', - url: this._tilerHost() + this.endPoint + (params.length ? "?" + params.join('&'): ''), - data: JSON.stringify(this.toJSON()), - success: function(data) { - loadingTime.end(); - // discard previous calls when there is another call waiting - if(0 === self._queue.length) { - callback(data); - } - self._requestFinished(); - }, - error: function(xhr) { - loadingTime.end(); - cartodb.core.Profiler.metric('cartodb-js.layergroup.post.error').inc(); - var err = { errors: ['unknow error'] }; - if (xhr.status === 0) { - err = { errors: ['connection error'] }; - } - try { - err = JSON.parse(xhr.responseText); - } catch(e) {} - if(0 === self._queue.length) { - callback(null, err); - } - self._requestFinished(); - } - }); - }, - - // returns the compressor depending on the size - // of the layer - _getCompressor: function(payload) { - var self = this; - if (this.options.compressor) { - return this.options.compressor; - } - - payload = payload || JSON.stringify(this.toJSON()); - if (!this.options.force_compress && payload.length < this.options.MAX_GET_SIZE) { - return function(data, level, callback) { - callback("config=" + encodeURIComponent(data)); - }; - } - - return function(data, level, callback) { - data = JSON.stringify({ config: data }); - LZMA.compress(data, level, function(encoded) { - callback("lzma=" + encodeURIComponent(self._array2hex(encoded))); - }); - }; - - }, - - _requestGET: function(params, callback) { - var self = this; - var ajax = this.options.ajax; - var json = JSON.stringify(this.toJSON()); - var compressor = this._getCompressor(json); - var endPoint = self.JSONPendPoint || self.endPoint; - compressor(json, 3, function(encoded) { - params.push(encoded); - var loadingTime = cartodb.core.Profiler.metric('cartodb-js.layergroup.get.time').start(); - var host = self.options.dynamic_cdn ? self._host(): self._tilerHost(); - ajax({ - dataType: 'jsonp', - url: host + endPoint + '?' + params.join('&'), - jsonpCallback: self.options.instanciateCallback, - cache: !!self.options.instanciateCallback, - success: function(data) { - loadingTime.end(); - if(0 === self._queue.length) { - // check for errors - if (data.error) { - cartodb.core.Profiler.metric('cartodb-js.layergroup.get.error').inc(); - callback(null, data.error); - } else { - callback(data); - } - } - self._requestFinished(); - }, - error: function(data) { - loadingTime.end(); - cartodb.core.Profiler.metric('cartodb-js.layergroup.get.error').inc(); - var err = { errors: ['unknow error'] }; - try { - err = JSON.parse(xhr.responseText); - } catch(e) {} - if(0 === self._queue.length) { - callback(null, err); - } - self._requestFinished(); - } - }); - }); - }, - - _getLayerToken: function(callback) { - var self = this; - var params = []; - callback = callback || function() {}; - - // if the previous request didn't finish, queue it - if(this._waiting) { - return this; - } - - this._queue = []; - - // when it's a named map the number of layers is not known - // so fetch the map - if (!this.named_map && this.visibleLayers().length === 0) { - callback(null); - return; - } - - // setup params - var extra_params = this.options.extra_params || {}; - var api_key = this.options.map_key || this.options.api_key || extra_params.map_key || extra_params.api_key; - if(api_key) { - params.push("map_key=" + api_key); - } - if(extra_params.auth_token) { - if (_.isArray(extra_params.auth_token)) { - for (var i = 0, len = extra_params.auth_token.length; i < len; i++) { - params.push("auth_token[]=" + extra_params.auth_token[i]); - } - } else { - params.push("auth_token=" + extra_params.auth_token); - } - } - - if (this.stat_tag) { - params.push("stat_tag=" + this.stat_tag); - } - // mark as the request is being done - this._waiting = true; - var req = null; - if (this._usePOST()) { - req = this._requestPOST; - } else { - req = this._requestGET; - } - req.call(this, params, callback); - return this; - }, - - _usePOST: function() { - if (this.options.cors) { - if (this.options.force_cors) { - return true; - } - // check payload size - var payload = JSON.stringify(this.toJSON()); - if (payload.length < this.options.MAX_GET_SIZE) { - return false; - } - } - return false; - }, - - - getLayer: function(index) { - return _.clone(this.layers[index]); - }, - - invalidate: function() { - this.layerToken = null; - this.urls = null; - this.onLayerDefinitionUpdated(); - }, - - setLayer: function(layer, def) { - if(layer < this.getLayerCount() && layer >= 0) { - if (def.options.hidden) { - var i = this.interactionEnabled[layer]; - if (i) { - def.interaction = true - this.setInteraction(layer, false); - } - } else { - if (this.layers[layer].interaction) { - this.setInteraction(layer, true); - delete this.layers[layer].interaction; - } - } - this.layers[layer] = _.clone(def); - } - this.invalidate(); - return this; - }, - - - getTiles: function(callback) { - var self = this; - if(self.layerToken) { - callback && callback(self._layerGroupTiles(self.layerToken, self.options.extra_params)); - return this; - } - this.getLayerToken(function(data, err) { - if(data) { - self.layerToken = data.layergroupid; - // if cdn_url is present, use it - if (data.cdn_url) { - var c = self.options.cdn_url = self.options.cdn_url || {}; - c.http = data.cdn_url.http || c.http; - c.https = data.cdn_url.https || c.https; - } - self.urls = self._layerGroupTiles(data.layergroupid, self.options.extra_params); - callback && callback(self.urls); - } else { - if (self.visibleLayers().length === 0) { - callback && callback({ - tiles: [Map.EMPTY_GIF], - grids: [] - }); - return; - } - callback && callback(null, err); - } - }); - return this; - }, - - isHttps: function() { - return this.options.tiler_protocol === 'https'; - }, - - _layerGroupTiles: function(layerGroupId, params) { - var subdomains = this.options.subdomains || ['0', '1', '2', '3']; - if(this.isHttps()) { - subdomains = [null]; // no subdomain - } - - var tileTemplate = '/{z}/{x}/{y}'; - - var grids = [] - var tiles = []; - - var pngParams = this._encodeParams(params, this.options.pngParams); - for(var i = 0; i < subdomains.length; ++i) { - var s = subdomains[i] - var cartodb_url = this._host(s) + Map.BASE_URL + '/' + layerGroupId - tiles.push(cartodb_url + tileTemplate + ".png" + (pngParams ? "?" + pngParams: '') ); - - var gridParams = this._encodeParams(params, this.options.gridParams); - for(var layer = 0; layer < this.layers.length; ++layer) { - grids[layer] = grids[layer] || []; - grids[layer].push(cartodb_url + "/" + layer + tileTemplate + ".grid.json" + (gridParams ? "?" + gridParams: '')); - } - } - - return { - tiles: tiles, - grids: grids - } - - }, - - _cleanInteractivity: function(attributes) { - if(!attributes) return; - if(typeof(attributes) == 'string') { - attributes = attributes.split(','); - } - - for(var i = 0; i < attributes.length; ++i) { - attributes[i] = attributes[i].replace(/ /g, ''); - } - - return attributes; - }, - - - onLayerDefinitionUpdated: function() {}, - - setSilent: function(b) { - this.silent = b; - }, - - _definitionUpdated: function() { - if(this.silent) return; - this.invalidate(); - }, - - _tileJSONfromTiles: function(layer, urls, options) { - options = options || {}; - var subdomains = options.subdomains || ['0', '1', '2', '3']; - - function replaceSubdomain(t) { - var tiles = []; - for (var i = 0; i < t.length; ++i) { - tiles.push(t[i].replace('{s}', subdomains[i % subdomains.length])); - } - return tiles; - } - - return { - tilejson: '2.0.0', - scheme: 'xyz', - grids: replaceSubdomain(urls.grids[layer]), - tiles: replaceSubdomain(urls.tiles), - formatter: function(options, data) { return data; } - }; - }, - - /** - * get tile json for layer - */ - getTileJSON: function(layer, callback) { - layer = layer == undefined ? 0: layer; - var self = this; - this.getTiles(function(urls) { - if(!urls) { - callback(null); - return; - } - if(callback) { - callback(self._tileJSONfromTiles(layer, urls)); - } - }); - }, - - /** - * Change query of the tiles - * @params {str} New sql for the tiles - */ - - _encodeParams: function(params, included) { - if(!params) return ''; - var url_params = []; - included = included || _.keys(params); - for(var i in included) { - var k = included[i] - var p = params[k]; - if(p) { - if (_.isArray(p)) { - for (var j = 0, len = p.length; j < len; j++) { - url_params.push(k + "[]=" + encodeURIComponent(p[j])); - } - } else { - var q = encodeURIComponent(p); - q = q.replace(/%7Bx%7D/g,"{x}").replace(/%7By%7D/g,"{y}").replace(/%7Bz%7D/g,"{z}"); - url_params.push(k + "=" + q); - } - } - } - return url_params.join('&') - }, - - - _tilerHost: function() { - var opts = this.options; - return opts.tiler_protocol + - "://" + ((opts.user_name) ? opts.user_name+".":"") + - opts.tiler_domain + - ((opts.tiler_port != "") ? (":" + opts.tiler_port) : ""); - }, - - _host: function(subhost) { - var opts = this.options; - if (opts.no_cdn) { - return this._tilerHost(); - } else { - var h = opts.tiler_protocol + "://"; - if (subhost) { - h += subhost + "."; - } - var cdn_host = opts.cdn_url || cdb.CDB_HOST; - if(!cdn_host.http && !cdn_host.https) { - throw new Error("cdn_host should contain http and/or https entries"); - } - h += cdn_host[opts.tiler_protocol] + "/" + opts.user_name; - return h; - } - }, - - getTooltipData: function(layer) { - return this.layers[layer].tooltip; - }, - - getInfowindowData: function(layer) { - var lyr; - var infowindow = this.layers[layer].infowindow; - if (!infowindow && this.options.layer_definition && (lyr = this.options.layer_definition.layers[layer])) { - infowindow = lyr.infowindow; - } - if (infowindow && infowindow.fields && infowindow.fields.length > 0) { - return infowindow; - } - return null; - }, - - containInfowindow: function() { - var layers = this.options.layer_definition.layers; - for(var i = 0; i < layers.length; ++i) { - var infowindow = layers[i].infowindow; - if (infowindow && infowindow.fields && infowindow.fields.length > 0) { - return true; - } - } - return false; - }, - - containTooltip: function() { - var layers = this.options.layer_definition.layers; - for(var i = 0; i < layers.length; ++i) { - var tooltip = layers[i].tooltip; - if (tooltip && tooltip.fields && tooltip.fields.length) { - return true; - } - } - return false; - }, - - getSubLayer: function(index) { - var layer = this.layers[index]; - layer.sub = layer.sub || new SubLayer(this, index); - return layer.sub; - }, - - getSubLayerCount: function() { - return this.getLayerCount(); - }, - - getSubLayers: function() { - var layers = [] - for (var i = 0; i < this.getSubLayerCount(); ++i) { - layers.push(this.getSubLayer(i)) - } - return layers; - } - -}; - -NamedMap.prototype = _.extend({}, Map.prototype, { - - setAuthToken: function(token) { - if(!this.isHttps()) { - throw new Error("https must be used when auth_token is set"); - } - this.options.extra_params = this.options.extra_params || {}; - this.options.extra_params.auth_token = token; - this.invalidate(); - return this; - }, - - setParams: function(attr, v) { - var params; - if (arguments.length === 2) { - params = {} - params[attr] = v; - } else { - params = attr; - } - if (!this.named_map.params) { - this.named_map.params = {}; - } - for (var k in params) { - if (params[k] === undefined || params[k] === null) { - delete this.named_map.params[k]; - } else { - this.named_map.params[k] = params[k]; - } - } - this.invalidate(); - return this; - }, - - toJSON: function() { - var p = this.named_map.params || {}; - for(var i = 0; i < this.layers.length; ++i) { - var layer = this.layers[i]; - p['layer' + i] = layer.options.hidden ? 0: 1; - } - return p; - }, - - containInfowindow: function() { - var layers = this.layers || []; - for(var i = 0; i < layers.length; ++i) { - var infowindow = layers[i].infowindow; - if (infowindow && infowindow.fields && infowindow.fields.length > 0) { - return true; - } - } - return false; - }, - - containTooltip: function() { - var layers = this.layers || []; - for(var i = 0; i < layers.length; ++i) { - var tooltip = layers[i].tooltip; - if (tooltip) { - return true; - } - } - return false; - }, - - _attributesUrl: function(layer, feature_id) { - // /api/maps/:map_id/:layer_index/attributes/:feature_id - var host = this.options.dynamic_cdn ? this._host(): this._tilerHost(); - var url = [ - host, - //'api', - //'v1', - Map.BASE_URL.slice(1), - this.layerToken, - layer, - 'attributes', - feature_id].join('/'); - - var extra_params = this.options.extra_params || {}; - var token = extra_params.auth_token; - if (token) { - if (_.isArray(token)) { - var tokenParams = []; - for (var i = 0, len = token.length; i < len; i++) { - tokenParams.push("auth_token[]=" + token[i]); - } - url += "?" + tokenParams.join('&') - } else { - url += "?auth_token=" + token - } - } - return url; - }, - - // for named maps attributes are fetch from attributes service - fetchAttributes: function(layer_index, feature_id, columnNames, callback) { - this._attrCallbackName = this._attrCallbackName || this._callbackName(); - var ajax = this.options.ajax; - var loadingTime = cartodb.core.Profiler.metric('cartodb-js.named_map.attributes.time').start(); - ajax({ - dataType: 'jsonp', - url: this._attributesUrl(layer_index, feature_id), - jsonpCallback: '_cdbi_layer_attributes_' + this._attrCallbackName, - cache: true, - success: function(data) { - loadingTime.end(); - callback(data); - }, - error: function(data) { - loadingTime.end(); - cartodb.core.Profiler.metric('cartodb-js.named_map.attributes.error').inc(); - callback(null); - } - }); - }, - - setSQL: function(sql) { - throw new Error("SQL is read-only in NamedMaps"); - }, - - setCartoCSS: function(sql) { - throw new Error("cartocss is read-only in NamedMaps"); - }, - - getCartoCSS: function() { - throw new Error("cartocss can't be accessed in NamedMaps"); - }, - - getSQL: function() { - throw new Error("SQL can't be accessed in NamedMaps"); - }, - - setLayer: function(layer, def) { - var not_allowed_attrs = {'sql': 1, 'cartocss': 1, 'interactivity': 1 }; - - for(var k in def.options) { - if (k in not_allowed_attrs) { - delete def.options[k]; - throw new Error( k + " is read-only in NamedMaps"); - } - } - return Map.prototype.setLayer.call(this, layer, def); - }, - - removeLayer: function(layer) { - throw new Error("sublayers are read-only in Named Maps"); - }, - - createSubLayer: function(attrs, options) { - throw new Error("sublayers are read-only in Named Maps"); - }, - - addLayer: function(def, layer) { - throw new Error("sublayers are read-only in Named Maps"); - }, - - // for named maps the layers are always the same (i.e they are - // not removed to hide) so the number does not change - getLayerIndexByNumber: function(number) { - return +number; - } - - -}); - -LayerDefinition.prototype = _.extend({}, Map.prototype, { - - setLayerDefinition: function(layerDefinition, options) { - options = options || {}; - this.version = layerDefinition.version || '1.0.0'; - this.stat_tag = layerDefinition.stat_tag; - this.layers = _.clone(layerDefinition.layers); - if(!options.silent) { - this._definitionUpdated(); - } - }, - - toJSON: function() { - var obj = {}; - obj.version = this.version; - if(this.stat_tag) { - obj.stat_tag = this.stat_tag; - } - obj.layers = []; - var layers = this.visibleLayers(); - for(var i = 0; i < layers.length; ++i) { - var layer = layers[i]; - obj.layers.push({ - type: 'cartodb', - options: { - sql: layer.options.sql, - cartocss: layer.options.cartocss, - cartocss_version: layer.options.cartocss_version || '2.1.0', - interactivity: this._cleanInteractivity(layer.options.interactivity) - } - }); - } - return obj; - }, - - removeLayer: function(layer) { - if(layer < this.getLayerCount() && layer >= 0) { - this.layers.splice(layer, 1); - this.interactionEnabled.splice(layer, 1); - this._reorderSubLayers(); - this.invalidate(); - } - return this; - }, - - _reorderSubLayers: function() { - for(var i = 0; i < this.layers.length; ++i) { - var layer = this.layers[i]; - if(layer.sub) { - layer.sub._setPosition(i); - } - } - }, - - addLayer: function(def, layer) { - layer = layer === undefined ? this.getLayerCount(): layer; - if(layer <= this.getLayerCount() && layer >= 0) { - if(!def.sql || !def.cartocss) { - throw new Error("layer definition should contain at least a sql and a cartocss"); - return this; - } - this.layers.splice(layer, 0, { - type: 'cartodb', - options: def - }); - this._definitionUpdated(); - } - return this; - }, - - /** - * set interactivity attributes for a layer. - * if attributes are passed as first param layer 0 is - * set - */ - setInteractivity: function(layer, attributes) { - if(attributes === undefined) { - attributes = layer; - layer = 0; - } - - if(layer >= this.getLayerCount() && layer < 0) { - throw new Error("layer does not exist"); - } - - if(typeof(attributes) == 'string') { - attributes = attributes.split(','); - } - - for(var i = 0; i < attributes.length; ++i) { - attributes[i] = attributes[i].replace(/ /g, ''); - } - - this.layers[layer].options.interactivity = attributes; - this._definitionUpdated(); - return this; - }, - - setQuery: function(layer, sql) { - if(sql === undefined) { - sql = layer; - layer = 0; - } - this.layers[layer].options.sql = sql - this._definitionUpdated(); - }, - - getQuery: function(layer) { - layer = layer || 0; - return this.layers[layer].options.sql - }, - - /** - * Change style of the tiles - * @params {style} New carto for the tiles - */ - setCartoCSS: function(layer, style, version) { - if(version === undefined) { - version = style; - style = layer; - layer = 0; - } - - version = version || cartodb.CARTOCSS_DEFAULT_VERSION; - - this.layers[layer].options.cartocss = style; - this.layers[layer].options.cartocss_version = version; - this._definitionUpdated(); - - }, - - /** - * adds a new sublayer to the layer with the sql and cartocss params - */ - createSubLayer: function(attrs, options) { - this.addLayer(attrs); - return this.getSubLayer(this.getLayerCount() - 1); - }, - - _getSqlApi: function(attrs) { - attrs = attrs || {}; - var port = attrs.sql_api_port - var domain = attrs.sql_api_domain + (port ? ':' + port: '') - var protocol = attrs.sql_api_protocol; - var version = 'v1'; - if (domain.indexOf('cartodb.com') !== -1) { - //protocol = 'http'; - domain = "cartodb.com"; - version = 'v2'; - } - - var sql = new cartodb.SQL({ - user: attrs.user_name, - protocol: protocol, - host: domain, - version: version - }); - - return sql; - }, - - fetchAttributes: function(layer_index, feature_id, columnNames, callback) { - var layer = this.getLayer(layer_index); - var sql = this._getSqlApi(this.options); - this._attrCallbackName = this._attrCallbackName || this._callbackName(); - - // prepare columns with double quotes - columnNames = _.map(columnNames, function(n) { - return "\"" + n + "\""; - }).join(','); - - var loadingTime = cartodb.core.Profiler.metric('cartodb-js.layergroup.attributes.time').start(); - // execute the sql - sql.execute('select {{{ fields }}} from ({{{ sql }}}) as _cartodbjs_alias where cartodb_id = {{{ cartodb_id }}}', { - fields: columnNames, - cartodb_id: feature_id, - sql: layer.options.sql - }, { - cache: true, // don't include timestamp - jsonpCallback: '_cdbi_layer_attributes_' + this._attrCallbackName, - jsonp: true - }).done(function(interact_data) { - loadingTime.end(); - if (interact_data.rows.length === 0 ) { - callback(null); - return; - } - callback(interact_data.rows[0]); - }).error(function() { - loadingTime.end(); - cartodb.core.Profiler.metric('cartodb-js.layergroup.attributes.error').inc(); - callback(null); - }); - } - - -}); - - -function SubLayer(_parent, position) { - this._parent = _parent; - this._position = position; - this._added = true; - this._bindInteraction(); - if (Backbone.Model) { - this.infowindow = new Backbone.Model(this._parent.getLayer(this._position).infowindow); - this.infowindow.bind('change', function() { - var def = this._parent.getLayer(this._position); - def.infowindow = this.infowindow.toJSON(); - this._parent.setLayer(this._position, def); - }, this); - } -} - -SubLayer.prototype = { - - remove: function() { - this._check(); - this._parent.removeLayer(this._position); - this._unbindInteraction(); - this._added = false; - }, - - toggle: function() { - this.get('hidden') ? this.show() : this.hide(); - return !this.get('hidden'); - }, - - show: function() { - if(this.get('hidden')) { - this.set({ - hidden: false - }); - } - }, - - hide: function() { - if(!this.get('hidden')) { - this.set({ - hidden: true - }); - } - }, - - setSQL: function(sql) { - return this.set({ - sql: sql - }); - }, - - setCartoCSS: function(cartocss) { - return this.set({ - cartocss: cartocss - }); - }, - - setInteractivity: function(fields) { - return this.set({ - interactivity: fields - }); - }, - - getSQL: function() { - return this.get('sql'); - }, - - getCartoCSS: function() { - return this.get('cartocss'); - }, - - setInteraction: function(active) { - this._parent.setInteraction(this._position, active); - }, - - get: function(attr) { - this._check(); - var attrs = this._parent.getLayer(this._position); - return attrs.options[attr]; - }, - - set: function(new_attrs) { - this._check(); - var def = this._parent.getLayer(this._position); - var attrs = def.options; - for(var i in new_attrs) { - attrs[i] = new_attrs[i]; - } - this._parent.setLayer(this._position, def); - return this; - }, - - unset: function(attr) { - var def = this._parent.getLayer(this._position); - delete def.options[attr]; - this._parent.setLayer(this._position, def); - }, - - _check: function() { - if(!this._added) throw "sublayer was removed"; - }, - - _unbindInteraction: function() { - if(!this._parent.off) return; - this._parent.off(null, null, this); - }, - - _bindInteraction: function() { - if(!this._parent.on) return; - var self = this; - // binds a signal to a layer event and trigger on this sublayer - // in case the position matches - var _bindSignal = function(signal, signalAlias) { - signalAlias = signalAlias || signal; - self._parent.on(signal, function() { - var args = Array.prototype.slice.call(arguments); - if (parseInt(args[args.length - 1], 10) == self._position) { - self.trigger.apply(self, [signalAlias].concat(args)); - } - }, self); - }; - _bindSignal('featureOver'); - _bindSignal('featureOut'); - _bindSignal('featureClick'); - _bindSignal('layermouseover', 'mouseover'); - _bindSignal('layermouseout', 'mouseout'); - }, - - _setPosition: function(p) { - this._position = p; - } - -}; - -// give events capabilitues -_.extend(SubLayer.prototype, Backbone.Events); - -/** utility methods to calculate hash */ -cartodb._makeCRCTable = function() { - var c; - var crcTable = []; - for(var n = 0; n < 256; ++n){ - c = n; - for(var k = 0; k < 8; ++k){ - c = ((c&1) ? (0xEDB88320 ^ (c >>> 1)) : (c >>> 1)); - } - crcTable[n] = c; - } - return crcTable; -} - -cartodb.crc32 = function(str) { - var crcTable = cartodb._crcTable || (cartodb._crcTable = cartodb._makeCRCTable()); - var crc = 0 ^ (-1); - - for (var i = 0, l = str.length; i < l; ++i ) { - crc = (crc >>> 8) ^ crcTable[(crc ^ str.charCodeAt(i)) & 0xFF]; - } - - return (crc ^ (-1)) >>> 0; -}; - -cartodb.uniqueCallbackName = function(str) { - cartodb._callback_c = cartodb._callback_c || 0; - ++cartodb._callback_c; - return cartodb.crc32(str) + "_" + cartodb._callback_c; -}; - - - - -/* - * common functions for cartodb connector - */ - -function CartoDBLayerCommon() { - - this.visible = true; - -} - -CartoDBLayerCommon.prototype = { - - // the way to show/hidelayer is to set opacity - // removing the interactivty at the same time - show: function() { - this.setOpacity(this.options.previous_opacity === undefined ? 0.99: this.options.previous_opacity); - delete this.options.previous_opacity; - this._interactionDisabled = false; - this.visible = true; - }, - - hide: function() { - if(this.options.previous_opacity == undefined) { - this.options.previous_opacity = this.options.opacity; - } - this.setOpacity(0); - // disable here interaction for all the layers - this._interactionDisabled = true; - this.visible = false; - }, - - toggle: function() { - - this.isVisible() ? this.hide() : this.show(); - - return this.isVisible(); - }, - - /** - * Returns if the layer is visible or not - */ - isVisible: function() { - return this.visible; - }, - - /** - * Active or desactive interaction - * @params enable {Number} layer number - * @params layer {Boolean} Choose if wants interaction or not - */ - setInteraction: function(layer, b) { - // shift arguments to maintain caompatibility - if(b == undefined) { - b = layer; - layer = 0; - } - var layerInteraction; - this.interactionEnabled[layer] = b; - if(!b) { - layerInteraction = this.interaction[layer]; - if(layerInteraction) { - layerInteraction.remove(); - this.interaction[layer] = null; - } - } else { - // if urls is null it means that setInteraction will be called - // when the layergroup token was recieved, then the real interaction - // layer will be created - if(this.urls) { - // generate the tilejson from the urls. wax needs it - var layer_index = this.getLayerIndexByNumber(+layer); - var tilejson = this._tileJSONfromTiles(layer_index, this.urls); - - // remove previous - layerInteraction = this.interaction[layer]; - if(layerInteraction) { - layerInteraction.remove(); - } - var self = this; - - // add the new one - this.interaction[layer] = this.interactionClass() - .map(this.options.map) - .tilejson(tilejson) - .on('on', function(o) { - if (self._interactionDisabled) return; - o.layer = +layer; - self._manageOnEvents(self.options.map, o); - }) - .on('off', function(o) { - if (self._interactionDisabled) return; - o = o || {} - o.layer = +layer; - self._manageOffEvents(self.options.map, o); - }); - } - } - return this; - }, - - setOptions: function (opts) { - - if (typeof opts != "object" || opts.length) { - throw new Error(opts + ' options must be an object'); - } - - _.extend(this.options, opts); - - var opts = this.options; - - this.options.query = this.options.query || "select * from " + this.options.table_name; - if(this.options.query_wrapper) { - this.options.query = _.template(this.options.query_wrapper)({ sql: this.options.query }); - } - - this.setSilent(true); - opts.interaction && this.setInteraction(opts.interaction); - opts.opacity && this.setOpacity(opts.opacity); - opts.query && this.setQuery(opts.query.replace(/\{\{table_name\}\}/g, this.options.table_name)); - opts.tile_style && this.setCartoCSS(opts.tile_style.replace(new RegExp( opts.table_name, "g"), "layer0")); - opts.cartocss && this.setCartoCSS(opts.cartocss); - opts.interactivity && this.setInteractivity(opts.interactivity); - opts.visible ? this.show() : this.hide(); - this.setSilent(false); - this._definitionUpdated(); - - }, - - _getLayerDefinition: function() { - // set params - var params = {}; - var opts = this.options; - var sql, cartocss, cartocss_version; - sql = opts.query || "select * from " + opts.table_name; - - if(opts.query_wrapper) { - sql = _.template(opts.query_wrapper)({ sql: sql }); - } - - cartocss = opts.tile_style; - cartocss_version = opts.cartocss_version || '2.1.0'; - - // extra_params? - for (var _param in opts.extra_params) { - var v = opts.extra_params[_param] - params[_param] = v.replace ? v.replace(/\{\{table_name\}\}/g, opts.table_name): v; - } - sql = sql.replace(/\{\{table_name\}\}/g, opts.table_name); - cartocss = cartocss.replace(/\{\{table_name\}\}/g, opts.table_name); - cartocss = cartocss.replace(new RegExp( opts.table_name, "g"), "layer0"); - - - return { - sql: sql, - cartocss: cartocss, - cartocss_version: cartocss_version, - params: params, - interactivity: opts.interactivity - } - - }, - - error: function(e) { - //console.log(e.error); - }, - - tilesOk: function() { - }, - - _clearInteraction: function() { - for(var i in this.interactionEnabled) { - if (this.interactionEnabled.hasOwnProperty(i) && - this.interactionEnabled[i]) { - this.setInteraction(i, false); - } - } - }, - - _reloadInteraction: function() { - for(var i in this.interactionEnabled) { - if (this.interactionEnabled.hasOwnProperty(i) && - this.interactionEnabled[i]) { - this.setInteraction(i, false); - this.setInteraction(i, true); - } - } - }, - - /** - * Check the tiles - */ - _checkTiles: function() { - var xyz = {z: 4, x: 6, y: 6} - , self = this - , img = new Image() - , urls = this._tileJSON() - - getTiles(function(urls) { - - var grid_url = urls.tiles[0] - .replace(/\{z\}/g,xyz.z) - .replace(/\{x\}/g,xyz.x) - .replace(/\{y\}/g,xyz.y); - - this.options.ajax({ - method: "get", - url: grid_url, - crossDomain: true, - success: function() { - self.tilesOk(); - clearTimeout(timeout) - }, - error: function(xhr, msg, data) { - clearTimeout(timeout); - self.error(xhr.responseText && JSON.parse(xhr.responseText)); - } - }); - }); - - var timeout = setTimeout(function(){ - clearTimeout(timeout); - self.error("tile timeout"); - }, 30000); - - } -}; - - - - -cdb.geo.common = {}; - -cdb.geo.common.CartoDBLogo = { - - /** - * Check if any class already exists - * in the provided container - */ - isWadusAdded: function(container, className) { - // Check if any cartodb-logo exists within container - var a = []; - var re = new RegExp('\\b' + className + '\\b'); - var els = container.getElementsByTagName("*"); - for(var i=0,j=els.length; i 0; - }, - - /** - * Check if browser supports retina images - */ - isRetinaBrowser: function() { - return ('devicePixelRatio' in window && window.devicePixelRatio > 1) || - ('matchMedia' in window && window.matchMedia('(min-resolution:144dpi)') && - window.matchMedia('(min-resolution:144dpi)').matches); - }, - - /** - * Add Cartodb logo - * It needs a position, timeout if it is needed and the container where to add it - */ - addWadus: function(position, timeout, container) { - var self = this; - setTimeout(function() { - if (!self.isWadusAdded(container, 'cartodb-logo')) { - var cartodb_link = document.createElement("div"); - var is_retina = self.isRetinaBrowser(); - cartodb_link.setAttribute('class','cartodb-logo'); - cartodb_link.setAttribute('style',"position:absolute; bottom:0; left:0; display:block; border:none; z-index:1000000;"); - var protocol = location.protocol.indexOf('https') === -1 ? 'http': 'https'; - var link = cdb.config.get('cartodb_logo_link'); - cartodb_link.innerHTML = "CartoDB"; - container.appendChild(cartodb_link); - } - },( timeout || 0 )); - } -}; - - -(function() { - /** - * base layer for all leaflet layers - */ - var LeafLetLayerView = function(layerModel, leafletLayer, leafletMap) { - this.leafletLayer = leafletLayer; - this.leafletMap = leafletMap; - this.model = layerModel; - - this.model.bind('change', this._modelUpdated, this); - this.type = layerModel.get('type') || layerModel.get('kind'); - this.type = this.type.toLowerCase(); - }; - - _.extend(LeafLetLayerView.prototype, Backbone.Events); - _.extend(LeafLetLayerView.prototype, { - - /** - * remove layer from the map and unbind events - */ - remove: function() { - this.leafletMap.removeLayer(this.leafletLayer); - this.model.unbind(null, null, this); - this.unbind(); - }, - /* - - show: function() { - this.leafletLayer.setOpacity(1.0); - }, - - hide: function() { - this.leafletLayer.setOpacity(0.0); - }, - */ - - /** - * reload the tiles - */ - reload: function() { - this.leafletLayer.redraw(); - } - - }); - - - cdb.geo.LeafLetLayerView = LeafLetLayerView; - - -})(); - -(function() { - -if(typeof(L) == "undefined") - return; - -/** - * this is a dummy layer class that modifies the leaflet DOM element background - * instead of creating a layer with div - */ -var LeafLetPlainLayerView = L.Class.extend({ - includes: L.Mixin.Events, - - initialize: function(layerModel, leafletMap) { - cdb.geo.LeafLetLayerView.call(this, layerModel, this, leafletMap); - }, - - onAdd: function() { - this.redraw(); - }, - - onRemove: function() { - var div = this.leafletMap.getContainer() - div.style.background = 'none'; - }, - - _modelUpdated: function() { - this.redraw(); - }, - - redraw: function() { - var div = this.leafletMap.getContainer() - div.style.backgroundColor = this.model.get('color') || '#FFF'; - - if (this.model.get('image')) { - var st = 'transparent url(' + this.model.get('image') + ') repeat center center'; - div.style.background = st - } - } -}); - -_.extend(LeafLetPlainLayerView.prototype, cdb.geo.LeafLetLayerView.prototype); - -cdb.geo.LeafLetPlainLayerView = LeafLetPlainLayerView; - -})(); - -(function() { - -if(typeof(L) == "undefined") - return; - -var LeafLetTiledLayerView = L.TileLayer.extend({ - initialize: function(layerModel, leafletMap) { - L.TileLayer.prototype.initialize.call(this, layerModel.get('urlTemplate'), { - tms: layerModel.get('tms'), - attribution: layerModel.get('attribution'), - minZoom: layerModel.get('minZoom'), - maxZoom: layerModel.get('maxZoom'), - subdomains: layerModel.get('subdomains') || 'abc', - errorTileUrl: layerModel.get('errorTileUrl'), - opacity: layerModel.get('opacity') - }); - cdb.geo.LeafLetLayerView.call(this, layerModel, this, leafletMap); - } - -}); - -_.extend(LeafLetTiledLayerView.prototype, cdb.geo.LeafLetLayerView.prototype, { - - _modelUpdated: function() { - _.defaults(this.leafletLayer.options, _.clone(this.model.attributes)); - this.leafletLayer.options.subdomains = this.model.get('subdomains') || 'abc'; - this.leafletLayer.options.attribution = this.model.get('attribution'); - this.leafletLayer.options.maxZoom = this.model.get('maxZoom'); - this.leafletLayer.options.minZoom = this.model.get('minZoom'); - // set url and reload - this.leafletLayer.setUrl(this.model.get('urlTemplate')); - } - -}); - -cdb.geo.LeafLetTiledLayerView = LeafLetTiledLayerView; - -})(); - -(function() { - - if(typeof(L) == "undefined") - return; - - var defaultSubstitute = function defaultSubstitute(lightOrDark) { - return { - url: 'http://{s}.basemaps.cartocdn.com/'+ (lightOrDark || "light") +'_all/{z}/{x}/{y}.png', - subdomains: 'abcd', - minZoom: 0, - maxZoom: 18, - attribution: 'Map designs by Stamen. Data by OpenStreetMap, Provided by CartoDB' - }; - }; - - var nokiaSubstitute = function nokiaSubstitute(type) { - return { - url: 'https://{s}.maps.nlp.nokia.com/maptile/2.1/maptile/newest/'+ type +'.day/{z}/{x}/{y}/256/png8?lg=eng&token=A7tBPacePg9Mj_zghvKt9Q&app_id=KuYppsdXZznpffJsKT24', - subdomains: '1234', - minZoom: 0, - maxZoom: 21, - attribution: '©2012 Nokia Terms of use' - }; - }; - - var substitutes = { - roadmap: defaultSubstitute(), - gray_roadmap: defaultSubstitute(), - dark_roadmap: defaultSubstitute('dark'), - hybrid: nokiaSubstitute('hybrid'), - terrain: nokiaSubstitute('terrain'), - satellite: nokiaSubstitute('satellite') - }; - - var LeafLetGmapsTiledLayerView = L.TileLayer.extend({ - initialize: function(layerModel, leafletMap) { - var substitute = substitutes[layerModel.get('base_type')]; - L.TileLayer.prototype.initialize.call(this, substitute.url, { - tms: false, - attribution: substitute.attribution, - minZoom: substitute.minZoom, - maxZoom: substitute.maxZoom, - subdomains: substitute.subdomains, - errorTileUrl: '', - opacity: 1 - }); - cdb.geo.LeafLetLayerView.call(this, layerModel, this, leafletMap); - } - - }); - - _.extend(LeafLetGmapsTiledLayerView.prototype, cdb.geo.LeafLetLayerView.prototype, { - - _modelUpdated: function() { - throw new Error("A GMaps baselayer should never be updated"); - } - - }); - - cdb.geo.LeafLetGmapsTiledLayerView = LeafLetGmapsTiledLayerView; - -})(); - -(function() { - -if(typeof(L) == "undefined") - return; - -var LeafLetWMSLayerView = L.TileLayer.WMS.extend({ - initialize: function(layerModel, leafletMap) { - - L.TileLayer.WMS.prototype.initialize.call(this, layerModel.get('urlTemplate'), { - attribution: layerModel.get('attribution'), - layers: layerModel.get('layers'), - format: layerModel.get('format'), - transparent: layerModel.get('transparent'), - minZoom: layerModel.get('minZomm'), - maxZoom: layerModel.get('maxZoom'), - subdomains: layerModel.get('subdomains') || 'abc', - errorTileUrl: layerModel.get('errorTileUrl'), - opacity: layerModel.get('opacity') - }); - - cdb.geo.LeafLetLayerView.call(this, layerModel, this, leafletMap); - } - -}); - -_.extend(LeafLetWMSLayerView.prototype, cdb.geo.LeafLetLayerView.prototype, { - - _modelUpdated: function() { - _.defaults(this.leafletLayer.options, _.clone(this.model.attributes)); - this.leafletLayer.setUrl(this.model.get('urlTemplate')); - } - -}); - -cdb.geo.LeafLetWMSLayerView = LeafLetWMSLayerView; - -})(); - -(function() { - -if(typeof(L) == "undefined") - return; - - -L.CartoDBGroupLayerBase = L.TileLayer.extend({ - - interactionClass: wax.leaf.interaction, - - includes: [ - cdb.geo.LeafLetLayerView.prototype, - //LayerDefinition.prototype, - CartoDBLayerCommon.prototype - ], - - options: { - opacity: 0.99, - attribution: "CartoDB", - debug: false, - visible: true, - added: false, - tiler_domain: "cartodb.com", - tiler_port: "80", - tiler_protocol: "http", - sql_api_domain: "cartodb.com", - sql_api_port: "80", - sql_api_protocol: "http", - maxZoom: 30, // default leaflet zoom level for a layers is 18, raise it - extra_params: { - }, - cdn_url: null, - subdomains: null - }, - - - initialize: function (options) { - options = options || {}; - // Set options - L.Util.setOptions(this, options); - - // Some checks - if (!options.layer_definition && !options.sublayers) { - throw new Error('cartodb-leaflet needs at least the layer_definition or sublayer list'); - } - - if(!options.layer_definition) { - this.options.layer_definition = LayerDefinition.layerDefFromSubLayers(options.sublayers); - } - - LayerDefinition.call(this, this.options.layer_definition, this.options); - - this.fire = this.trigger; - - CartoDBLayerCommon.call(this); - L.TileLayer.prototype.initialize.call(this); - this.interaction = []; - this.addProfiling(); - }, - - addProfiling: function() { - this.bind('tileloadstart', function(e) { - var s = this.tileStats || (this.tileStats = {}); - s[e.tile.src] = cartodb.core.Profiler.metric('cartodb-js.tile.png.load.time').start(); - }); - var finish = function(e) { - var s = this.tileStats && this.tileStats[e.tile.src]; - s && s.end(); - }; - this.bind('tileload', finish); - this.bind('tileerror', function(e) { - cartodb.core.Profiler.metric('cartodb-js.tile.png.error').inc(); - finish(e); - }); - }, - - - // overwrite getTileUrl in order to - // support different tiles subdomains in tilejson way - getTileUrl: function (tilePoint) { - var EMPTY_GIF = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7"; - this._adjustTilePoint(tilePoint); - - var tiles = [EMPTY_GIF]; - if(this.tilejson) { - tiles = this.tilejson.tiles; - } - - var index = (tilePoint.x + tilePoint.y) % tiles.length; - - return L.Util.template(tiles[index], L.Util.extend({ - z: this._getZoomForUrl(), - x: tilePoint.x, - y: tilePoint.y - }, this.options)); - }, - - /** - * Change opacity of the layer - * @params {Integer} New opacity - */ - setOpacity: function(opacity) { - - if (isNaN(opacity) || opacity>1 || opacity<0) { - throw new Error(opacity + ' is not a valid value'); - } - - // Leaflet only accepts 0-0.99... Weird! - this.options.opacity = Math.min(opacity, 0.99); - - if (this.options.visible) { - L.TileLayer.prototype.setOpacity.call(this, this.options.opacity); - this.fire('updated'); - } - }, - - - /** - * When Leaflet adds the layer... go! - * @params {map} - */ - onAdd: function(map) { - var self = this; - this.options.map = map; - - // Add cartodb logo - if (this.options.cartodb_logo != false) - cdb.geo.common.CartoDBLogo.addWadus({ left:8, bottom:8 }, 0, map._container); - - this.__update(function() { - // if while the layer was processed in the server is removed - // it should not be added to the map - var id = L.stamp(self); - if (!map._layers[id]) { - return; - } - - L.TileLayer.prototype.onAdd.call(self, map); - self.fire('added'); - self.options.added = true; - }); - }, - - - /** - * When removes the layer, destroy interactivity if exist - */ - onRemove: function(map) { - if(this.options.added) { - this.options.added = false; - L.TileLayer.prototype.onRemove.call(this, map); - } - }, - - /** - * Update CartoDB layer - * generates a new url for tiles and refresh leaflet layer - * do not collide with leaflet _update - */ - __update: function(done) { - var self = this; - this.fire('updated'); - this.fire('loading'); - var map = this.options.map; - - this.getTiles(function(urls, err) { - if(urls) { - self.tilejson = urls; - self.setUrl(self.tilejson.tiles[0]); - // manage interaction - self._reloadInteraction(); - self.ok && self.ok(); - done && done(); - } else { - self.error && self.error(err); - done && done(); - } - }); - }, - - - _checkLayer: function() { - if (!this.options.added) { - throw new Error('the layer is not still added to the map'); - } - }, - - /** - * Set a new layer attribution - * @params {String} New attribution string - */ - setAttribution: function(attribution) { - this._checkLayer(); - - // Remove old one - this.map.attributionControl.removeAttribution(this.options.attribution); - - // Set new attribution in the options - this.options.attribution = attribution; - - // Change text - this.map.attributionControl.addAttribution(this.options.attribution); - - // Change in the layer - this.options.attribution = this.options.attribution; - this.tilejson.attribution = this.options.attribution; - - this.fire('updated'); - }, - - /** - * Bind events for wax interaction - * @param {Object} Layer map object - * @param {Event} Wax event - */ - _manageOnEvents: function(map, o) { - var layer_point = this._findPos(map,o), - latlng = map.layerPointToLatLng(layer_point); - var event_type = o.e.type.toLowerCase(); - - - var screenPos = map.layerPointToContainerPoint(layer_point); - - switch (event_type) { - case 'mousemove': - if (this.options.featureOver) { - return this.options.featureOver(o.e,latlng, screenPos, o.data, o.layer); - } - break; - - case 'click': - case 'touchend': - case 'mspointerup': - if (this.options.featureClick) { - this.options.featureClick(o.e,latlng, screenPos, o.data, o.layer); - } - break; - default: - break; - } - }, - - - /** - * Bind off event for wax interaction - */ - _manageOffEvents: function(map, o) { - if (this.options.featureOut) { - return this.options.featureOut && this.options.featureOut(o.e, o.layer); - } - }, - - /** - * Get the Leaflet Point of the event - * @params {Object} Map object - * @params {Object} Wax event object - */ - _findPos: function (map,o) { - var curleft = 0, curtop = 0; - var obj = map.getContainer(); - - var x, y; - if (o.e.changedTouches && o.e.changedTouches.length > 0) { - x = o.e.changedTouches[0].clientX + window.scrollX; - y = o.e.changedTouches[0].clientY + window.scrollY; - } else { - x = o.e.clientX; - y = o.e.clientY; - } - - if (obj.offsetParent) { - // Modern browsers - do { - curleft += obj.offsetLeft; - curtop += obj.offsetTop; - } while (obj = obj.offsetParent); - return map.containerPointToLayerPoint(new L.Point(x - curleft, y - curtop)); - } else { - var rect = obj.getBoundingClientRect(); - var p = new L.Point( - o.e.clientX - rect.left - obj.clientLeft - window.scrollX, - o.e.clientY - rect.top - obj.clientTop - window.scrollY); - return map.containerPointToLayerPoint(p); - } - } - -}); - -L.CartoDBGroupLayer = L.CartoDBGroupLayerBase.extend({ - includes: [ - LayerDefinition.prototype, - ] -}); - -function layerView(base) { - var layerViewClass = base.extend({ - - includes: [ - cdb.geo.LeafLetLayerView.prototype, - Backbone.Events - ], - - initialize: function(layerModel, leafletMap) { - var self = this; - var hovers = []; - - // CartoDB new attribution, - // also we have the logo - layerModel.attributes.attribution = cdb.config.get('cartodb_attributions'); - - var opts = _.clone(layerModel.attributes); - - opts.map = leafletMap; - - var // preserve the user's callbacks - _featureOver = opts.featureOver, - _featureOut = opts.featureOut, - _featureClick = opts.featureClick; - - var previousEvent; - var eventTimeout = -1; - - opts.featureOver = function(e, latlon, pxPos, data, layer) { - if (!hovers[layer]) { - self.trigger('layerenter', e, latlon, pxPos, data, layer); - } - hovers[layer] = 1; - _featureOver && _featureOver.apply(this, arguments); - self.featureOver && self.featureOver.apply(self, arguments); - // if the event is the same than before just cancel the event - // firing because there is a layer on top of it - if (e.timeStamp === previousEvent) { - clearTimeout(eventTimeout); - } - eventTimeout = setTimeout(function() { - self.trigger('mouseover', e, latlon, pxPos, data, layer); - self.trigger('layermouseover', e, latlon, pxPos, data, layer); - }, 0); - previousEvent = e.timeStamp; - - }; - - opts.featureOut = function(m, layer) { - if (hovers[layer]) { - self.trigger('layermouseout', layer); - } - hovers[layer] = 0; - if(!_.any(hovers)) { - self.trigger('mouseout'); - } - _featureOut && _featureOut.apply(this, arguments); - self.featureOut && self.featureOut.apply(self, arguments); - }; - - opts.featureClick = _.debounce(function() { - _featureClick && _featureClick.apply(self, arguments); - self.featureClick && self.featureClick.apply(self, arguments); - }, 10); - - base.prototype.initialize.call(this, opts); - cdb.geo.LeafLetLayerView.call(this, layerModel, this, leafletMap); - - }, - - featureOver: function(e, latlon, pixelPos, data, layer) { - // dont pass leaflet lat/lon - this.trigger('featureOver', e, [latlon.lat, latlon.lng], pixelPos, data, layer); - }, - - featureOut: function(e, layer) { - this.trigger('featureOut', e, layer); - }, - - featureClick: function(e, latlon, pixelPos, data, layer) { - // dont pass leaflet lat/lon - this.trigger('featureClick', e, [latlon.lat, latlon.lng], pixelPos, data, layer); - }, - - error: function(e) { - this.trigger('error', e ? e.errors : 'unknown error'); - this.model.trigger('error', e?e.errors:'unknown error'); - }, - - ok: function(e) { - this.model.trigger('tileOk'); - }, - - onLayerDefinitionUpdated: function() { - this.__update(); - } - - }); - - return layerViewClass; -} - -L.NamedMap = L.CartoDBGroupLayerBase.extend({ - includes: [ - cdb.geo.LeafLetLayerView.prototype, - NamedMap.prototype, - CartoDBLayerCommon.prototype - ], - - initialize: function (options) { - options = options || {}; - // Set options - L.Util.setOptions(this, options); - - // Some checks - if (!options.named_map && !options.sublayers) { - throw new Error('cartodb-leaflet needs at least the named_map'); - } - - /*if(!options.layer_definition) { - this.options.layer_definition = LayerDefinition.layerDefFromSubLayers(options.sublayers); - }*/ - - NamedMap.call(this, this.options.named_map, this.options); - - this.fire = this.trigger; - - CartoDBLayerCommon.call(this); - L.TileLayer.prototype.initialize.call(this); - this.interaction = []; - this.addProfiling(); - } -}); - -cdb.geo.LeafLetCartoDBLayerGroupView = layerView(L.CartoDBGroupLayer); -cdb.geo.LeafLetCartoDBNamedMapView = layerView(L.NamedMap); - -})(); - -(function() { - -if(typeof(L) == "undefined") - return; - -L.CartoDBLayer = L.CartoDBGroupLayer.extend({ - - options: { - query: "SELECT * FROM {{table_name}}", - opacity: 0.99, - attribution: "CartoDB", - debug: false, - visible: true, - added: false, - extra_params: {}, - layer_definition_version: '1.0.0' - }, - - - initialize: function (options) { - L.Util.setOptions(this, options); - - if (!options.table_name || !options.user_name || !options.tile_style) { - throw ('cartodb-leaflet needs at least a CartoDB table name, user_name and tile_style'); - } - - L.CartoDBGroupLayer.prototype.initialize.call(this, { - layer_definition: { - version: this.options.layer_definition_version, - layers: [{ - type: 'cartodb', - options: this._getLayerDefinition(), - infowindow: this.options.infowindow - }] - } - }); - - this.setOptions(this.options); - }, - - setQuery: function(layer, sql) { - if(sql === undefined) { - sql = layer; - layer = 0; - } - sql = sql || 'select * from ' + this.options.table_name; - LayerDefinition.prototype.setQuery.call(this, layer, sql); - }, - - /** - * Returns if the layer is visible or not - */ - isVisible: function() { - return this.visible; - }, - - - /** - * Returns if the layer belongs to the map - */ - isAdded: function() { - return this.options.added; - } - -}); - -/** - * leatlet cartodb layer - */ - -var LeafLetLayerCartoDBView = L.CartoDBLayer.extend({ - //var LeafLetLayerCartoDBView = function(layerModel, leafletMap) { - initialize: function(layerModel, leafletMap) { - var self = this; - - _.bindAll(this, 'featureOut', 'featureOver', 'featureClick'); - - // CartoDB new attribution, - // also we have the logo - layerModel.attributes.attribution = cdb.config.get('cartodb_attributions'); - - var opts = _.clone(layerModel.attributes); - - opts.map = leafletMap; - - var // preserve the user's callbacks - _featureOver = opts.featureOver, - _featureOut = opts.featureOut, - _featureClick = opts.featureClick; - - opts.featureOver = function() { - _featureOver && _featureOver.apply(this, arguments); - self.featureOver && self.featureOver.apply(this, arguments); - }; - - opts.featureOut = function() { - _featureOut && _featureOut.apply(this, arguments); - self.featureOut && self.featureOut.apply(this, arguments); - }; - - opts.featureClick = function() { - _featureClick && _featureClick.apply(this, arguments); - self.featureClick && self.featureClick.apply(opts, arguments); - }; - - layerModel.bind('change:visible', function() { - self.model.get('visible') ? self.show(): self.hide(); - }, this); - - L.CartoDBLayer.prototype.initialize.call(this, opts); - cdb.geo.LeafLetLayerView.call(this, layerModel, this, leafletMap); - - }, - - _modelUpdated: function() { - var attrs = _.clone(this.model.attributes); - this.leafletLayer.setOptions(attrs); - }, - - featureOver: function(e, latlon, pixelPos, data) { - // dont pass leaflet lat/lon - this.trigger('featureOver', e, [latlon.lat, latlon.lng], pixelPos, data, 0); - }, - - featureOut: function(e) { - this.trigger('featureOut', e, 0); - }, - - featureClick: function(e, latlon, pixelPos, data) { - // dont pass leaflet lat/lon - this.trigger('featureClick', e, [latlon.lat, latlon.lng], pixelPos, data, 0); - }, - - reload: function() { - this.model.invalidate(); - //this.redraw(); - }, - - error: function(e) { - this.trigger('error', e?e.error:'unknown error'); - this.model.trigger('tileError', e?e.error:'unknown error'); - }, - - tilesOk: function(e) { - this.model.trigger('tileOk'); - }, - - includes: [ - cdb.geo.LeafLetLayerView.prototype, - Backbone.Events - ] - -}); - -/*_.extend(L.CartoDBLayer.prototype, CartoDBLayerCommon.prototype); - -_.extend( - LeafLetLayerCartoDBView.prototype, - cdb.geo.LeafLetLayerView.prototype, - L.CartoDBLayer.prototype, - Backbone.Events, // be sure this is here to not use the on/off from leaflet - - */ -cdb.geo.LeafLetLayerCartoDBView = LeafLetLayerCartoDBView; - -})(); -/** -* leaflet implementation of a map -*/ -(function() { - - if(typeof(L) == "undefined") - return; - - /** - * leatlef impl - */ - cdb.geo.LeafletMapView = cdb.geo.MapView.extend({ - - - initialize: function() { - - _.bindAll(this, '_addLayer', '_removeLayer', '_setZoom', '_setCenter', '_setView'); - - cdb.geo.MapView.prototype.initialize.call(this); - - var self = this; - - var center = this.map.get('center'); - - var mapConfig = { - zoomControl: false, - center: new L.LatLng(center[0], center[1]), - zoom: this.map.get('zoom'), - minZoom: this.map.get('minZoom'), - maxZoom: this.map.get('maxZoom') - }; - - - if (this.map.get('bounding_box_ne')) { - //mapConfig.maxBounds = [this.map.get('bounding_box_ne'), this.map.get('bounding_box_sw')]; - } - - if (!this.options.map_object) { - - this.map_leaflet = new L.Map(this.el, mapConfig); - - // remove the "powered by leaflet" - this.map_leaflet.attributionControl.setPrefix(''); - - // Disable the scrollwheel - if (this.map.get("scrollwheel") == false) this.map_leaflet.scrollWheelZoom.disable(); - - } else { - - this.map_leaflet = this.options.map_object; - this.setElement(this.map_leaflet.getContainer()); - - var c = self.map_leaflet.getCenter(); - - self._setModelProperty({ center: [c.lat, c.lng] }); - self._setModelProperty({ zoom: self.map_leaflet.getZoom() }); - - // unset bounds to not change mapbounds - self.map.unset('view_bounds_sw', { silent: true }); - self.map.unset('view_bounds_ne', { silent: true }); - - } - - this.map.bind('set_view', this._setView, this); - this.map.layers.bind('add', this._addLayer, this); - this.map.layers.bind('remove', this._removeLayer, this); - this.map.layers.bind('reset', this._addLayers, this); - this.map.layers.bind('change:type', this._swicthLayerView, this); - - this.map.geometries.bind('add', this._addGeometry, this); - this.map.geometries.bind('remove', this._removeGeometry, this); - - this._bindModel(); - - this._addLayers(); - - this.map_leaflet.on('layeradd', function(lyr) { - this.trigger('layeradd', lyr, self); - }, this); - - this.map_leaflet.on('zoomstart', function() { - self.trigger('zoomstart'); - }); - - this.map_leaflet.on('click', function(e) { - self.trigger('click', e.originalEvent, [e.latlng.lat, e.latlng.lng]); - }); - - this.map_leaflet.on('dblclick', function(e) { - self.trigger('dblclick', e.originalEvent); - }); - - this.map_leaflet.on('zoomend', function() { - self._setModelProperty({ - zoom: self.map_leaflet.getZoom() - }); - self.trigger('zoomend'); - }, this); - - this.map_leaflet.on('move', function() { - var c = self.map_leaflet.getCenter(); - self._setModelProperty({ center: [c.lat, c.lng] }); - }); - - this.map_leaflet.on('drag', function() { - var c = self.map_leaflet.getCenter(); - self._setModelProperty({ - center: [c.lat, c.lng] - }); - self.trigger('drag'); - }, this); - - this.map.bind('change:maxZoom', function() { - L.Util.setOptions(self.map_leaflet, { maxZoom: self.map.get('maxZoom') }); - }, this); - - this.map.bind('change:minZoom', function() { - L.Util.setOptions(self.map_leaflet, { minZoom: self.map.get('minZoom') }); - }, this); - - this.trigger('ready'); - - // looks like leaflet dont like to change the bounds just after the inicialization - var bounds = this.map.getViewBounds(); - - if (bounds) { - this.showBounds(bounds); - } - }, - - clean: function() { - //see https://github.com/CloudMade/Leaflet/issues/1101 - L.DomEvent.off(window, 'resize', this.map_leaflet._onResize, this.map_leaflet); - - // remove layer views - for(var layer in this.layers) { - var layer_view = this.layers[layer]; - layer_view.remove(); - delete this.layers[layer]; - } - - // do not change by elder - cdb.core.View.prototype.clean.call(this); - }, - - _setScrollWheel: function(model, z) { - if (z) { - this.map_leaflet.scrollWheelZoom.enable(); - } else { - this.map_leaflet.scrollWheelZoom.disable(); - } - }, - - _setZoom: function(model, z) { - this._setView(); - }, - - _setCenter: function(model, center) { - this._setView(); - }, - - _setView: function() { - this.map_leaflet.setView(this.map.get("center"), this.map.get("zoom") || 0 ); - }, - - _addGeomToMap: function(geom) { - var geo = cdb.geo.LeafletMapView.createGeometry(geom); - geo.geom.addTo(this.map_leaflet); - return geo; - }, - - _removeGeomFromMap: function(geo) { - this.map_leaflet.removeLayer(geo.geom); - }, - - createLayer: function(layer) { - return cdb.geo.LeafletMapView.createLayer(layer, this.map_leaflet); - }, - - _addLayer: function(layer, layers, opts) { - var self = this; - var lyr, layer_view; - - layer_view = cdb.geo.LeafletMapView.createLayer(layer, this.map_leaflet); - if(!layer_view) { - return; - } - - var appending = !opts || opts.index === undefined || opts.index === _.size(this.layers); - // since leaflet does not support layer ordering - // add the layers should be removed and added again - // if the layer is being appended do not clear - if(!appending) { - for(var i in this.layers) { - this.map_leaflet.removeLayer(this.layers[i]); - } - } - - this.layers[layer.cid] = layer_view; - - // add them again, in correct order - if(appending) { - cdb.geo.LeafletMapView.addLayerToMap(layer_view, self.map_leaflet); - if(layer_view.setZIndex) { - layer_view.setZIndex(layer.get('order')) - } - } else { - this.map.layers.each(function(layerModel) { - var v = self.layers[layerModel.cid]; - if(v) { - cdb.geo.LeafletMapView.addLayerToMap(v, self.map_leaflet); - if(v.setZIndex) { - v.setZIndex(layerModel.get('order')) - } - } - }); - } - - var attribution = layer.get('attribution'); - - if (attribution) { - // Setting attribution in map model - var attributions = this.map.get('attribution') || []; - if (!_.contains(attributions, attribution)) { - attributions.push(attribution); - } - - this.map.set({ attribution: attributions }); - } - - if(opts == undefined || !opts.silent) { - this.trigger('newLayerView', layer_view, layer, this); - } - return layer_view; - }, - - pixelToLatLon: function(pos) { - var point = this.map_leaflet.containerPointToLatLng([pos[0], pos[1]]); - return point; - }, - - latLonToPixel: function(latlon) { - var point = this.map_leaflet.latLngToLayerPoint(new L.LatLng(latlon[0], latlon[1])); - return this.map_leaflet.layerPointToContainerPoint(point); - }, - - // return the current bounds of the map view - getBounds: function() { - var b = this.map_leaflet.getBounds(); - var sw = b.getSouthWest(); - var ne = b.getNorthEast(); - return [ - [sw.lat, sw.lng], - [ne.lat, ne.lng] - ]; - }, - - setAttribution: function(m) { - // Leaflet takes care of attribution by its own. - }, - - getSize: function() { - return this.map_leaflet.getSize(); - }, - - panBy: function(p) { - this.map_leaflet.panBy(new L.Point(p.x, p.y)); - }, - - setCursor: function(cursor) { - $(this.map_leaflet.getContainer()).css('cursor', cursor); - }, - - getNativeMap: function() { - return this.map_leaflet; - }, - - invalidateSize: function() { - // there is a race condition in leaflet. If size is invalidated - // and at the same time the center is set the final center is displaced - // so set pan to false so the map is not moved and then force the map - // to be at the place it should be - this.map_leaflet.invalidateSize({ pan: false })//, animate: false }); - this.map_leaflet.setView(this.map.get("center"), this.map.get("zoom") || 0, { - animate: false - }); - } - - }, { - - layerTypeMap: { - "tiled": cdb.geo.LeafLetTiledLayerView, - "wms": cdb.geo.LeafLetWMSLayerView, - "cartodb": cdb.geo.LeafLetLayerCartoDBView, - "carto": cdb.geo.LeafLetLayerCartoDBView, - "plain": cdb.geo.LeafLetPlainLayerView, - - // Substitutes the GMaps baselayer w/ an equivalent Leaflet tiled layer, since not supporting Gmaps anymore - "gmapsbase": cdb.geo.LeafLetGmapsTiledLayerView, - - "layergroup": cdb.geo.LeafLetCartoDBLayerGroupView, - "namedmap": cdb.geo.LeafLetCartoDBNamedMapView, - "torque": function(layer, map) { - return new cdb.geo.LeafLetTorqueLayer(layer, map); - } - }, - - createLayer: function(layer, map) { - var layer_view = null; - var layerClass = this.layerTypeMap[layer.get('type').toLowerCase()]; - - if (layerClass) { - try { - layer_view = new layerClass(layer, map); - } catch(e) { - cdb.log.error("MAP: error creating layer" + layer.get('type') + " " + e); - } - } else { - cdb.log.error("MAP: " + layer.get('type') + " can't be created"); - } - return layer_view; - }, - - addLayerToMap: function(layer_view, map, pos) { - map.addLayer(layer_view.leafletLayer); - if(pos !== undefined) { - if(v.setZIndex) { - v.setZIndex(pos); - } - } - }, - - /** - * create the view for the geometry model - */ - createGeometry: function(geometryModel) { - if(geometryModel.isPoint()) { - return new cdb.geo.leaflet.PointView(geometryModel); - } - return new cdb.geo.leaflet.PathView(geometryModel); - } - - }); - - // set the image path in order to be able to get leaflet icons - // code adapted from leaflet - L.Icon.Default.imagePath = (function () { - var scripts = document.getElementsByTagName('script'), - leafletRe = /\/?cartodb[\-\._]?([\w\-\._]*)\.js\??/; - - var i, len, src, matches; - - for (i = 0, len = scripts.length; i < len; i++) { - src = scripts[i].src; - matches = src.match(leafletRe); - - if (matches) { - var bits = src.split('/') - delete bits[bits.length - 1]; - return bits.join('/') + 'themes/css/images'; - } - } - }()); - -})(); - -(function() { - -if(typeof(google) == "undefined" || typeof(google.maps) == "undefined") - return; - -/** -* base layer for all google maps -*/ - -var GMapsLayerView = function(layerModel, gmapsLayer, gmapsMap) { - this.gmapsLayer = gmapsLayer; - this.map = this.gmapsMap = gmapsMap; - this.model = layerModel; - this.model.bind('change', this._update, this); - - this.type = layerModel.get('type') || layerModel.get('kind'); - this.type = this.type.toLowerCase(); -}; - -_.extend(GMapsLayerView.prototype, Backbone.Events); -_.extend(GMapsLayerView.prototype, { - - // hack function to search layer inside google maps layers - _searchLayerIndex: function() { - var self = this; - var index = -1; - this.gmapsMap.overlayMapTypes.forEach( - function(layer, i) { - if (layer == self) { - index = i; - } - } - ); - return index; - }, - - /** - * remove layer from the map and unbind events - */ - remove: function() { - if(!this.isBase) { - var self = this; - var idx = this._searchLayerIndex(); - if(idx >= 0) { - this.gmapsMap.overlayMapTypes.removeAt(idx); - } else if (this.gmapsLayer.setMap){ - this.gmapsLayer.setMap(null); - } - this.model.unbind(null, null, this); - this.unbind(); - } - }, - - refreshView: function() { - var self = this; - //reset to update - if(this.isBase) { - var a = '_baseLayer'; - this.gmapsMap.setMapTypeId(null); - this.gmapsMap.mapTypes.set(a, this.gmapsLayer); - this.gmapsMap.setMapTypeId(a); - } else { - var idx = this._searchLayerIndex(); - if(idx >= 0) { - this.gmapsMap.overlayMapTypes.setAt(idx, this); - } - } - }, - - reload: function() { this.refreshView() ; }, - _update: function() { this.refreshView(); } - - -}); - -cdb.geo.GMapsLayerView = GMapsLayerView; - -})(); - -(function() { - -if(typeof(google) == "undefined" || typeof(google.maps) == "undefined") - return; - -var GMapsBaseLayerView = function(layerModel, gmapsMap) { - cdb.geo.GMapsLayerView.call(this, layerModel, null, gmapsMap); -}; - -_.extend( - GMapsBaseLayerView.prototype, - cdb.geo.GMapsLayerView.prototype, - { - _update: function() { - var m = this.model; - var types = { - "roadmap": google.maps.MapTypeId.ROADMAP, - "gray_roadmap": google.maps.MapTypeId.ROADMAP, - "dark_roadmap": google.maps.MapTypeId.ROADMAP, - "hybrid": google.maps.MapTypeId.HYBRID, - "satellite": google.maps.MapTypeId.SATELLITE, - "terrain": google.maps.MapTypeId.TERRAIN - }; - - this.gmapsMap.setOptions({ - mapTypeId: types[m.get('base_type')] - }); - - this.gmapsMap.setOptions({ - styles: m.get('style') || DEFAULT_MAP_STYLE - }); - }, - remove: function() { } -}); - - -cdb.geo.GMapsBaseLayerView = GMapsBaseLayerView; - - -})(); - -(function() { - -if(typeof(google) == "undefined" || typeof(google.maps) == "undefined") - return; - -var GMapsPlainLayerView = function(layerModel, gmapsMap) { - this.color = layerModel.get('color') - cdb.geo.GMapsLayerView.call(this, layerModel, this, gmapsMap); -}; - -_.extend( - GMapsPlainLayerView.prototype, - cdb.geo.GMapsLayerView.prototype, { - - _update: function() { - this.color = this.model.get('color') - this.refreshView(); - }, - - getTile: function(coord, zoom, ownerDocument) { - var div = document.createElement('div'); - div.style.width = this.tileSize.x; - div.style.height = this.tileSize.y; - div['background-color'] = this.color; - return div; - }, - - tileSize: new google.maps.Size(256,256), - maxZoom: 100, - minZoom: 0, - name:"plain layer", - alt: "plain layer" -}); - -cdb.geo.GMapsPlainLayerView = GMapsPlainLayerView; - -})(); - -(function() { - -if(typeof(google) == "undefined" || typeof(google.maps) == "undefined") - return; - -// TILED LAYER -var GMapsTiledLayerView = function(layerModel, gmapsMap) { - cdb.geo.GMapsLayerView.call(this, layerModel, this, gmapsMap); - this.tileSize = new google.maps.Size(256, 256); - this.opacity = 1.0; - this.isPng = true; - this.maxZoom = 22; - this.minZoom = 0; - this.name= 'cartodb tiled layer'; - google.maps.ImageMapType.call(this, this); -}; - -_.extend( - GMapsTiledLayerView.prototype, - cdb.geo.GMapsLayerView.prototype, - google.maps.ImageMapType.prototype, { - - getTileUrl: function(tile, zoom) { - var y = tile.y; - var tileRange = 1 << zoom; - if (y < 0 || y >= tileRange) { - return null; - } - var x = tile.x; - if (x < 0 || x >= tileRange) { - x = (x % tileRange + tileRange) % tileRange; - } - if(this.model.get('tms')) { - y = tileRange - y - 1; - } - var urlPattern = this.model.get('urlTemplate'); - return urlPattern - .replace("{x}",x) - .replace("{y}",y) - .replace("{z}",zoom); - } -}); - -cdb.geo.GMapsTiledLayerView = GMapsTiledLayerView; - - -})(); -(function() { -// if google maps is not defined do not load the class -if(typeof(google) == "undefined" || typeof(google.maps) == "undefined") { - return; -} - -// helper to get pixel position from latlon -var Projector = function(map) { this.setMap(map); }; -Projector.prototype = new google.maps.OverlayView(); -Projector.prototype.draw = function() {}; -Projector.prototype.latLngToPixel = function(point) { - var p = this.getProjection(); - if(p) { - return p.fromLatLngToContainerPixel(point); - } - return [0, 0]; -}; -Projector.prototype.pixelToLatLng = function(point) { - var p = this.getProjection(); - if(p) { - return p.fromContainerPixelToLatLng(point); - } - return [0, 0]; - //return this.map.getProjection().fromPointToLatLng(point); -}; - -var default_options = { - opacity: 0.99, - attribution: "CartoDB", - debug: false, - visible: true, - added: false, - tiler_domain: "cartodb.com", - tiler_port: "80", - tiler_protocol: "http", - sql_api_domain: "cartodb.com", - sql_api_port: "80", - sql_api_protocol: "http", - extra_params: { - }, - cdn_url: null, - subdomains: null -}; - -var OPACITY_FILTER = "progid:DXImageTransform.Microsoft.gradient(startColorstr=#00FFFFFF,endColorstr=#00FFFFFF)"; - -var CartoDBNamedMap = function(opts) { - - this.options = _.defaults(opts, default_options); - this.tiles = 0; - this.tilejson = null; - this.interaction = []; - - if (!opts.named_map && !opts.sublayers) { - throw new Error('cartodb-gmaps needs at least the named_map'); - } - - // Add CartoDB logo - if (this.options.cartodb_logo != false) - cdb.geo.common.CartoDBLogo.addWadus({ left: 74, bottom:8 }, 2000, this.options.map.getDiv()); - - wax.g.connector.call(this, opts); - - // lovely wax connector overwrites options so set them again - // TODO: remove wax.connector here - _.extend(this.options, opts); - this.projector = new Projector(opts.map); - NamedMap.call(this, this.options.named_map, this.options); - CartoDBLayerCommon.call(this); - // precache - this.update(); -}; - - -var CartoDBLayerGroup = function(opts) { - - this.options = _.defaults(opts, default_options); - this.tiles = 0; - this.tilejson = null; - this.interaction = []; - - if (!opts.layer_definition && !opts.sublayers) { - throw new Error('cartodb-leaflet needs at least the layer_definition or sublayer list'); - } - - // if only sublayers is available, generate layer_definition from it - if(!opts.layer_definition) { - opts.layer_definition = LayerDefinition.layerDefFromSubLayers(opts.sublayers); - } - - // Add CartoDB logo - if (this.options.cartodb_logo != false) - cdb.geo.common.CartoDBLogo.addWadus({ left: 74, bottom:8 }, 2000, this.options.map.getDiv()); - - wax.g.connector.call(this, opts); - - // lovely wax connector overwrites options so set them again - // TODO: remove wax.connector here - _.extend(this.options, opts); - this.projector = new Projector(opts.map); - LayerDefinition.call(this, opts.layer_definition, this.options); - CartoDBLayerCommon.call(this); - // precache - this.update(); -}; - -function setImageOpacityIE8(img, opacity) { - var v = Math.round(opacity*100); - if (v >= 99) { - img.style.filter = OPACITY_FILTER; - } else { - img.style.filter = "alpha(opacity=" + (opacity) + ");"; - } -} - -function CartoDBLayerGroupBase() {} - -CartoDBLayerGroupBase.prototype.setOpacity = function(opacity) { - if (isNaN(opacity) || opacity > 1 || opacity < 0) { - throw new Error(opacity + ' is not a valid value, should be in [0, 1] range'); - } - this.opacity = this.options.opacity = opacity; - for(var key in this.cache) { - var img = this.cache[key]; - img.style.opacity = opacity; - setImageOpacityIE8(img, opacity); - } - -}; - -CartoDBLayerGroupBase.prototype.setAttribution = function() {}; - -CartoDBLayerGroupBase.prototype.getTile = function(coord, zoom, ownerDocument) { - var EMPTY_GIF = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7"; - - var self = this; - var ie = 'ActiveXObject' in window, - ielt9 = ie && !document.addEventListener; - - this.options.added = true; - - if(this.tilejson === null) { - var key = zoom + '/' + coord.x + '/' + coord.y; - var i = this.cache[key] = new Image(256, 256); - i.src = EMPTY_GIF; - i.setAttribute('gTileKey', key); - i.style.opacity = this.options.opacity; - return i; - } - - var im = wax.g.connector.prototype.getTile.call(this, coord, zoom, ownerDocument); - - // in IE8 semi transparency does not work and needs filter - if( ielt9 ) { - setImageOpacityIE8(im, this.options.opacity); - } - im.style.opacity = this.options.opacity; - if (this.tiles === 0) { - this.loading && this.loading(); - } - - this.tiles++; - - var loadTime = cartodb.core.Profiler.metric('cartodb-js.tile.png.load.time').start(); - - var finished = function() { - loadTime.end(); - self.tiles--; - if (self.tiles === 0) { - self.finishLoading && self.finishLoading(); - } - }; - im.onload = finished; - im.onerror = function() { - cartodb.core.Profiler.metric('cartodb-js.tile.png.error').inc(); - finished(); - } - - - return im; -}; - -CartoDBLayerGroupBase.prototype.onAdd = function () { - //this.update(); -}; - -CartoDBLayerGroupBase.prototype.clear = function () { - this._clearInteraction(); - self.finishLoading && self.finishLoading(); -}; - -CartoDBLayerGroupBase.prototype.update = function (done) { - var self = this; - this.loading && this.loading(); - this.getTiles(function(urls, err) { - if(urls) { - self.tilejson = urls; - self.options.tiles = urls.tiles; - self.tiles = 0; - self.cache = {}; - self._reloadInteraction(); - self.refreshView(); - self.ok && self.ok(); - done && done(); - } else { - self.error && self.error(err) - } - }); -}; - - -CartoDBLayerGroupBase.prototype.refreshView = function() { - var self = this; - var map = this.options.map; - map.overlayMapTypes.forEach( - function(layer, i) { - if (layer == self) { - map.overlayMapTypes.setAt(i, self); - return; - } - } - ); -} -CartoDBLayerGroupBase.prototype.onLayerDefinitionUpdated = function() { - this.update(); -} - -CartoDBLayerGroupBase.prototype._checkLayer = function() { - if (!this.options.added) { - throw new Error('the layer is not still added to the map'); - } -} - -CartoDBLayerGroupBase.prototype._findPos = function (map,o) { - var curleft, cartop; - curleft = curtop = 0; - var obj = map.getDiv(); - - var x, y; - if (o.e.changedTouches && o.e.changedTouches.length > 0) { - x = o.e.changedTouches[0].clientX + window.scrollX; - y = o.e.changedTouches[0].clientY + window.scrollY; - } else { - x = o.e.clientX; - y = o.e.clientY; - } - - do { - curleft += obj.offsetLeft; - curtop += obj.offsetTop; - } while (obj = obj.offsetParent); - return new google.maps.Point( - x - curleft, - y - curtop - ); -}; - -CartoDBLayerGroupBase.prototype._manageOffEvents = function(map, o){ - if (this.options.featureOut) { - return this.options.featureOut && this.options.featureOut(o.e, o.layer); - } -}; - - -CartoDBLayerGroupBase.prototype._manageOnEvents = function(map,o) { - var point = this._findPos(map, o); - var latlng = this.projector.pixelToLatLng(point); - var event_type = o.e.type.toLowerCase(); - - - switch (event_type) { - case 'mousemove': - if (this.options.featureOver) { - return this.options.featureOver(o.e,latlng, point, o.data, o.layer); - } - break; - - case 'click': - case 'touchend': - case 'mspointerup': - if (this.options.featureClick) { - this.options.featureClick(o.e,latlng, point, o.data, o.layer); - } - break; - default: - break; - } -} - -// CartoDBLayerGroup type -CartoDBLayerGroup.Projector = Projector; -CartoDBLayerGroup.prototype = new wax.g.connector(); -_.extend(CartoDBLayerGroup.prototype, LayerDefinition.prototype, CartoDBLayerGroupBase.prototype, CartoDBLayerCommon.prototype); -CartoDBLayerGroup.prototype.interactionClass = wax.g.interaction; - - -// CartoDBNamedMap -CartoDBNamedMap.prototype = new wax.g.connector(); -_.extend(CartoDBNamedMap.prototype, NamedMap.prototype, CartoDBLayerGroupBase.prototype, CartoDBLayerCommon.prototype); -CartoDBNamedMap.prototype.interactionClass = wax.g.interaction; - - -// export -cdb.geo.CartoDBLayerGroupGMaps = CartoDBLayerGroup; -cdb.geo.CartoDBNamedMapGMaps = CartoDBNamedMap; - -/* - * - * cartodb layer group view - * - */ - -function LayerGroupView(base) { - var GMapsCartoDBLayerGroupView = function(layerModel, gmapsMap) { - var self = this; - var hovers = []; - - _.bindAll(this, 'featureOut', 'featureOver', 'featureClick'); - - // CartoDB new attribution,z - // also we have the logo - layerModel.attributes.attribution = cdb.config.get('cartodb_attributions'); - - var opts = _.clone(layerModel.attributes); - - opts.map = gmapsMap; - - var // preserve the user's callbacks - _featureOver = opts.featureOver, - _featureOut = opts.featureOut, - _featureClick = opts.featureClick; - - var previousEvent; - var eventTimeout = -1; - - opts.featureOver = function(e, latlon, pxPos, data, layer) { - if (!hovers[layer]) { - self.trigger('layerenter', e, latlon, pxPos, data, layer); - } - hovers[layer] = 1; - _featureOver && _featureOver.apply(this, arguments); - self.featureOver && self.featureOver.apply(this, arguments); - - // if the event is the same than before just cancel the event - // firing because there is a layer on top of it - if (e.timeStamp === previousEvent) { - clearTimeout(eventTimeout); - } - eventTimeout = setTimeout(function() { - self.trigger('mouseover', e, latlon, pxPos, data, layer); - self.trigger('layermouseover', e, latlon, pxPos, data, layer); - }, 0); - previousEvent = e.timeStamp; - }; - - opts.featureOut = function(m, layer) { - if (hovers[layer]) { - self.trigger('layermouseout', layer); - } - hovers[layer] = 0; - if(!_.any(hovers)) { - self.trigger('mouseout'); - } - _featureOut && _featureOut.apply(this, arguments); - self.featureOut && self.featureOut.apply(this, arguments); - }; - - opts.featureClick = _.debounce(function() { - _featureClick && _featureClick.apply(this, arguments); - self.featureClick && self.featureClick.apply(opts, arguments); - }, 10); - - - //CartoDBLayerGroup.call(this, opts); - base.call(this, opts); - cdb.geo.GMapsLayerView.call(this, layerModel, this, gmapsMap); - }; - - - - _.extend( - GMapsCartoDBLayerGroupView.prototype, - cdb.geo.GMapsLayerView.prototype, - base.prototype, - { - - _update: function() { - this.setOptions(this.model.attributes); - }, - - reload: function() { - this.model.invalidate(); - }, - - remove: function() { - cdb.geo.GMapsLayerView.prototype.remove.call(this); - this.clear(); - }, - - featureOver: function(e, latlon, pixelPos, data, layer) { - // dont pass gmaps LatLng - this.trigger('featureOver', e, [latlon.lat(), latlon.lng()], pixelPos, data, layer); - }, - - featureOut: function(e, layer) { - this.trigger('featureOut', e, layer); - }, - - featureClick: function(e, latlon, pixelPos, data, layer) { - // dont pass leaflet lat/lon - this.trigger('featureClick', e, [latlon.lat(), latlon.lng()], pixelPos, data, layer); - }, - - error: function(e) { - if(this.model) { - //trigger the error form _checkTiles in the model - this.model.trigger('error', e?e.errors:'unknown error'); - this.model.trigger('tileError', e?e.errors:'unknown error'); - } - }, - - ok: function(e) { - this.model.trigger('tileOk'); - }, - - tilesOk: function(e) { - this.model.trigger('tileOk'); - }, - - loading: function() { - this.trigger("loading"); - }, - - finishLoading: function() { - this.trigger("load"); - } - - - }); - return GMapsCartoDBLayerGroupView; -} - -cdb.geo.GMapsCartoDBLayerGroupView = LayerGroupView(CartoDBLayerGroup); -cdb.geo.GMapsCartoDBNamedMapView = LayerGroupView(CartoDBNamedMap); - -cdb.geo.CartoDBNamedMapGMaps = CartoDBNamedMap; -/** -* gmaps cartodb layer -*/ - -})(); -(function() { -// if google maps is not defined do not load the class -if(typeof(google) == "undefined" || typeof(google.maps) == "undefined") - return; - -// helper to get pixel position from latlon -var Projector = function(map) { this.setMap(map); }; -Projector.prototype = new google.maps.OverlayView(); -Projector.prototype.draw = function() {}; -Projector.prototype.latLngToPixel = function(point) { - var p = this.getProjection(); - if(p) { - return p.fromLatLngToContainerPixel(point); - } - return [0, 0]; -}; -Projector.prototype.pixelToLatLng = function(point) { - var p = this.getProjection(); - if(p) { - return p.fromContainerPixelToLatLng(point); - } - return [0, 0]; - //return this.map.getProjection().fromPointToLatLng(point); -}; - -var CartoDBLayer = function(options) { - - var default_options = { - query: "SELECT * FROM {{table_name}}", - opacity: 0.99, - attribution: "CartoDB", - opacity: 1, - debug: false, - visible: true, - added: false, - extra_params: {}, - layer_definition_version: '1.0.0' - }; - - this.options = _.defaults(options, default_options); - - if (!options.table_name || !options.user_name || !options.tile_style) { - throw ('cartodb-gmaps needs at least a CartoDB table name, user_name and tile_style'); - } - - - this.options.layer_definition = { - version: this.options.layer_definition_version, - layers: [{ - type: 'cartodb', - options: this._getLayerDefinition(), - infowindow: this.options.infowindow - }] - }; - cdb.geo.CartoDBLayerGroupGMaps.call(this, this.options); - - this.setOptions(this.options); - -}; - -_.extend(CartoDBLayer.prototype, cdb.geo.CartoDBLayerGroupGMaps.prototype); - -CartoDBLayer.prototype.setQuery = function (layer, sql) { - if(sql === undefined) { - sql = layer; - layer = 0; - } - sql = sql || 'select * from ' + this.options.table_name; - LayerDefinition.prototype.setQuery.call(this, layer, sql); -}; - -cdb.geo.CartoDBLayerGMaps = CartoDBLayer; - -/** -* gmaps cartodb layer -*/ - -var GMapsCartoDBLayerView = function(layerModel, gmapsMap) { - var self = this; - - _.bindAll(this, 'featureOut', 'featureOver', 'featureClick'); - - // CartoDB new attribution, - // also we have the logo - layerModel.attributes.attribution = cdb.config.get('cartodb_attributions'); - - var opts = _.clone(layerModel.attributes); - - opts.map = gmapsMap; - - var // preserve the user's callbacks - _featureOver = opts.featureOver, - _featureOut = opts.featureOut, - _featureClick = opts.featureClick; - - opts.featureOver = function() { - _featureOver && _featureOver.apply(this, arguments); - self.featureOver && self.featureOver.apply(this, arguments); - }; - - opts.featureOut = function() { - _featureOut && _featureOut.apply(this, arguments); - self.featureOut && self.featureOut.apply(this, arguments); - }; - - opts.featureClick = function() { - _featureClick && _featureClick.apply(this, arguments); - self.featureClick && self.featureClick.apply(opts, arguments); - }; - - cdb.geo.CartoDBLayerGMaps.call(this, opts); - cdb.geo.GMapsLayerView.call(this, layerModel, this, gmapsMap); -}; - -cdb.geo.GMapsCartoDBLayerView = GMapsCartoDBLayerView; - - -_.extend( - GMapsCartoDBLayerView.prototype, - cdb.geo.CartoDBLayerGMaps.prototype, - cdb.geo.GMapsLayerView.prototype, - { - - _update: function() { - this.setOptions(this.model.attributes); - }, - - reload: function() { - this.model.invalidate(); - }, - - remove: function() { - cdb.geo.GMapsLayerView.prototype.remove.call(this); - this.clear(); - }, - - featureOver: function(e, latlon, pixelPos, data) { - // dont pass gmaps LatLng - this.trigger('featureOver', e, [latlon.lat(), latlon.lng()], pixelPos, data, 0); - }, - - featureOut: function(e) { - this.trigger('featureOut', e); - }, - - featureClick: function(e, latlon, pixelPos, data, layer) { - // dont pass leaflet lat/lon - this.trigger('featureClick', e, [latlon.lat(), latlon.lng()], pixelPos, data, 0); - }, - - error: function(e) { - if(this.model) { - //trigger the error form _checkTiles in the model - this.model.trigger('error', e?e.error:'unknown error'); - this.model.trigger('tileError', e?e.error:'unknown error'); - } - }, - - tilesOk: function(e) { - this.model.trigger('tileOk'); - }, - - loading: function() { - this.trigger("loading"); - }, - - finishLoading: function() { - this.trigger("load"); - } - - -}); - -})(); - -// if google maps is not defined do not load the class -if(typeof(google) != "undefined" && typeof(google.maps) != "undefined") { - - var DEFAULT_MAP_STYLE = [ { stylers: [ { saturation: -65 }, { gamma: 1.52 } ] },{ featureType: "administrative", stylers: [ { saturation: -95 }, { gamma: 2.26 } ] },{ featureType: "water", elementType: "labels", stylers: [ { visibility: "off" } ] },{ featureType: "administrative.locality", stylers: [ { visibility: "off" } ] },{ featureType: "road", stylers: [ { visibility: "simplified" }, { saturation: -99 }, { gamma: 2.22 } ] },{ featureType: "poi", elementType: "labels", stylers: [ { visibility: "off" } ] },{ featureType: "road.arterial", stylers: [ { visibility: "off" } ] },{ featureType: "road.local", elementType: "labels", stylers: [ { visibility: "off" } ] },{ featureType: "transit", stylers: [ { visibility: "off" } ] },{ featureType: "road", elementType: "labels", stylers: [ { visibility: "off" } ] },{ featureType: "poi", stylers: [ { saturation: -55 } ] } ]; - - - - cdb.geo.GoogleMapsMapView = cdb.geo.MapView.extend({ - - layerTypeMap: { - "tiled": cdb.geo.GMapsTiledLayerView, - "cartodb": cdb.geo.GMapsCartoDBLayerView, - "carto": cdb.geo.GMapsCartoDBLayerView, - "plain": cdb.geo.GMapsPlainLayerView, - "gmapsbase": cdb.geo.GMapsBaseLayerView, - "layergroup": cdb.geo.GMapsCartoDBLayerGroupView, - "namedmap": cdb.geo.GMapsCartoDBNamedMapView, - "torque": function(layer, map) { - return new cdb.geo.GMapsTorqueLayerView(layer, map); - }, - "wms": cdb.geo.LeafLetWMSLayerView - }, - - initialize: function() { - _.bindAll(this, '_ready'); - this._isReady = false; - var self = this; - - cdb.geo.MapView.prototype.initialize.call(this); - - var bounds = this.map.getViewBounds(); - - if (bounds) { - this.showBounds(bounds); - } - - var center = this.map.get('center'); - - if (!this.options.map_object) { - - this.map_googlemaps = new google.maps.Map(this.el, { - center: new google.maps.LatLng(center[0], center[1]), - zoom: this.map.get('zoom'), - minZoom: this.map.get('minZoom'), - maxZoom: this.map.get('maxZoom'), - disableDefaultUI: true, - scrollwheel: this.map.get("scrollwheel"), - mapTypeControl:false, - mapTypeId: google.maps.MapTypeId.ROADMAP, - backgroundColor: 'white', - tilt: 0 - }); - - this.map.bind('change:maxZoom', function() { - self.map_googlemaps.setOptions({ maxZoom: self.map.get('maxZoom') }); - }, this); - - this.map.bind('change:minZoom', function() { - self.map_googlemaps.setOptions({ minZoom: self.map.get('minZoom') }); - }, this); - - } else { - - this.map_googlemaps = this.options.map_object; - this.setElement(this.map_googlemaps.getDiv()); - - // fill variables - var c = self.map_googlemaps.getCenter(); - - self._setModelProperty({ center: [c.lat(), c.lng()] }); - self._setModelProperty({ zoom: self.map_googlemaps.getZoom() }); - - // unset bounds to not change mapbounds - self.map.unset('view_bounds_sw', { silent: true }); - self.map.unset('view_bounds_ne', { silent: true }); - - } - - this.map.geometries.bind('add', this._addGeometry, this); - this.map.geometries.bind('remove', this._removeGeometry, this); - - - this._bindModel(); - this._addLayers(); - - google.maps.event.addListener(this.map_googlemaps, 'center_changed', function() { - var c = self.map_googlemaps.getCenter(); - self._setModelProperty({ center: [c.lat(), c.lng()] }); - }); - - google.maps.event.addListener(this.map_googlemaps, 'zoom_changed', function() { - self._setModelProperty({ - zoom: self.map_googlemaps.getZoom() - }); - }); - - google.maps.event.addListener(this.map_googlemaps, 'click', function(e) { - self.trigger('click', e, [e.latLng.lat(), e.latLng.lng()]); - }); - - google.maps.event.addListener(this.map_googlemaps, 'dblclick', function(e) { - self.trigger('dblclick', e); - }); - - this.map.layers.bind('add', this._addLayer, this); - this.map.layers.bind('remove', this._removeLayer, this); - this.map.layers.bind('reset', this._addLayers, this); - this.map.layers.bind('change:type', this._swicthLayerView, this); - - this.projector = new cdb.geo.CartoDBLayerGroupGMaps.Projector(this.map_googlemaps); - - this.projector.draw = this._ready; - - }, - - _ready: function() { - this.projector.draw = function() {}; - this.trigger('ready'); - this._isReady = true; - }, - - _setScrollWheel: function(model, z) { - this.map_googlemaps.setOptions({ scrollwheel: z }); - }, - - _setZoom: function(model, z) { - z = z || 0; - this.map_googlemaps.setZoom(z); - }, - - _setCenter: function(model, center) { - var c = new google.maps.LatLng(center[0], center[1]); - this.map_googlemaps.setCenter(c); - }, - - createLayer: function(layer) { - var layer_view, - layerClass = this.layerTypeMap[layer.get('type').toLowerCase()]; - - if (layerClass) { - try { - layer_view = new layerClass(layer, this.map_googlemaps); - } catch(e) { - cdb.log.error("MAP: error creating layer" + layer.get('type') + " " + e); - } - } else { - cdb.log.error("MAP: " + layer.get('type') + " can't be created"); - } - return layer_view; - }, - - _addLayer: function(layer, layers, opts) { - opts = opts || {}; - var self = this; - var lyr, layer_view; - - layer_view = this.createLayer(layer); - - if (!layer_view) { - return; - } - - this.layers[layer.cid] = layer_view; - - if (layer_view) { - var idx = _(this.layers).filter(function(lyr) { return !!lyr.getTile; }).length - 1; - var isBaseLayer = _.keys(this.layers).length === 1 || (opts && opts.index === 0); - // set base layer - if(isBaseLayer && !opts.no_base_layer) { - var m = layer_view.model; - if(m.get('type') === 'GMapsBase') { - layer_view._update(); - } else { - layer_view.isBase = true; - layer_view._update(); - } - } else { - idx -= 1; - idx = Math.max(0, idx); // avoid -1 - if (layer_view.getTile) { - if (!layer_view.gmapsLayer) { - cdb.log.error("gmaps layer can't be null"); - } - self.map_googlemaps.overlayMapTypes.setAt(idx, layer_view.gmapsLayer); - } else { - layer_view.gmapsLayer.setMap(self.map_googlemaps); - } - } - if(opts === undefined || !opts.silent) { - this.trigger('newLayerView', layer_view, layer, this); - } - } else { - cdb.log.error("layer type not supported"); - } - - var attribution = layer.get('attribution'); - - if (attribution) { - // Setting attribution in map model - // it doesn't persist in the backend, so this is needed. - var attributions = this.map.get('attribution') || []; - if (!_.contains(attributions, attribution)) { - attributions.push(attribution); - } - - this.map.set({ attribution: attributions }); - } - - return layer_view; - - }, - - pixelToLatLon: function(pos) { - return this.projector.fromContainerPixelToLatLng(new google.maps.Point(pos[0], pos[1])); - }, - - latLonToPixel: function(latlon) { - return this.projector.latLngToPixel(new google.maps.LatLng(latlon[0], latlon[1])); - }, - - getSize: function() { - return { - x: this.$el.width(), - y: this.$el.height() - }; - }, - - panBy: function(p) { - var c = this.map.get('center'); - var pc = this.latLonToPixel(c); - p.x += pc.x; - p.y += pc.y; - var ll = this.projector.pixelToLatLng(p); - this.map.setCenter([ll.lat(), ll.lng()]); - }, - - getBounds: function() { - if(this._isReady) { - var b = this.map_googlemaps.getBounds(); - var sw = b.getSouthWest(); - var ne = b.getNorthEast(); - return [ - [sw.lat(), sw.lng()], - [ne.lat(), ne.lng()] - ]; - } - return [ [0,0], [0,0] ]; - }, - - setAttribution: function(m) { - // Remove old one - var old = document.getElementById("cartodb-gmaps-attribution") - , attribution = m.get("attribution").join(", "); - - // If div already exists, remove it - if (old) { - old.parentNode.removeChild(old); - } - - // Add new one - var container = this.map_googlemaps.getDiv() - , cartodb_attribution = document.createElement("div"); - - cartodb_attribution.setAttribute('id','cartodb-gmaps-attribution'); - cartodb_attribution.setAttribute('class', 'gmaps'); - container.appendChild(cartodb_attribution); - cartodb_attribution.innerHTML = attribution; - }, - - setCursor: function(cursor) { - this.map_googlemaps.setOptions({ draggableCursor: cursor }); - }, - - _addGeomToMap: function(geom) { - var geo = cdb.geo.GoogleMapsMapView.createGeometry(geom); - if(geo.geom.length) { - for(var i = 0 ; i < geo.geom.length; ++i) { - geo.geom[i].setMap(this.map_googlemaps); - } - } else { - geo.geom.setMap(this.map_googlemaps); - } - return geo; - }, - - _removeGeomFromMap: function(geo) { - if(geo.geom.length) { - for(var i = 0 ; i < geo.geom.length; ++i) { - geo.geom[i].setMap(null); - } - } else { - geo.geom.setMap(null); - } - }, - - getNativeMap: function() { - return this.map_googlemaps; - }, - - invalidateSize: function() { - google.maps.event.trigger(this.map_googlemaps, 'resize'); - } - - }, { - - addLayerToMap: function(layer, map, pos) { - pos = pos || 0; - if (!layer) { - cdb.log.error("gmaps layer can't be null"); - } - if (layer.getTile) { - map.overlayMapTypes.setAt(pos, layer); - } else { - layer.setMap(map); - } - }, - - /** - * create the view for the geometry model - */ - createGeometry: function(geometryModel) { - if(geometryModel.isPoint()) { - return new cdb.geo.gmaps.PointView(geometryModel); - } - return new cdb.geo.gmaps.PathView(geometryModel); - } - }); - -} -/** - * generic dialog - * - * this opens a dialog in the middle of the screen rendering - * a dialog using cdb.templates 'common/dialog' or template_base option. - * - * inherit class should implement render_content (it could return another widget) - * - * usage example: - * - * var MyDialog = cdb.ui.common.Dialog.extend({ - * render_content: function() { - * return "my content"; - * }, - * }) - * var dialog = new MyDialog({ - * title: 'test', - * description: 'long description here', - * template_base: $('#base_template').html(), - * width: 500 - * }); - * - * $('body').append(dialog.render().el); - * dialog.open(); - * - * TODO: implement draggable - * TODO: modal - * TODO: document modal_type - */ - -cdb.ui.common.Dialog = cdb.core.View.extend({ - - tagName: 'div', - className: 'dialog', - - events: { - 'click .ok': '_ok', - 'click .cancel': '_cancel', - 'click .close': '_cancel' - }, - - default_options: { - title: 'title', - description: '', - ok_title: 'Ok', - cancel_title: 'Cancel', - width: 300, - height: 200, - clean_on_hide: false, - enter_to_confirm: false, - template_name: 'common/views/dialog_base', - ok_button_classes: 'button green', - cancel_button_classes: '', - modal_type: '', - modal_class: '', - include_footer: true, - additionalButtons: [] - }, - - initialize: function() { - _.defaults(this.options, this.default_options); - - _.bindAll(this, 'render', '_keydown'); - - // Keydown bindings for the dialog - $(document).bind('keydown', this._keydown); - - // After removing the dialog, cleaning other bindings - this.bind("clean", this._reClean); - - this.template_base = this.options.template_base ? _.template(this.options.template_base) : cdb.templates.getTemplate(this.options.template_name); - }, - - render: function() { - var $el = this.$el; - - $el.html(this.template_base(this.options)); - - $el.find(".modal").css({ - width: this.options.width - //height: this.options.height - //'margin-left': -this.options.width>>1, - //'margin-top': -this.options.height>>1 - }); - - if(this.render_content) { - - this.$('.content').append(this.render_content()); - } - - if(this.options.modal_class) { - this.$el.addClass(this.options.modal_class); - } - - return this; - }, - - - _keydown: function(e) { - // If clicks esc, goodbye! - if (e.keyCode === 27) { - this._cancel(); - // If clicks enter, same as you click on ok button. - } else if (e.keyCode === 13 && this.options.enter_to_confirm) { - this._ok(); - } - }, - - /** - * helper method that renders the dialog and appends it to body - */ - appendToBody: function() { - $('body').append(this.render().el); - return this; - }, - - _ok: function(ev) { - - if(ev) ev.preventDefault(); - - if (this.ok) { - this.ok(this.result); - } - - this.hide(); - - }, - - _cancel: function(ev) { - - if (ev) { - ev.preventDefault(); - ev.stopPropagation(); - } - - if (this.cancel) { - this.cancel(); - } - - this.hide(); - - }, - - hide: function() { - - this.$el.hide(); - - if (this.options.clean_on_hide) { - this.clean(); - } - - }, - - open: function() { - - this.$el.show(); - - }, - - _reClean: function() { - - $(document).unbind('keydown', this._keydown); - - } - -}); - -cdb.ui.common.ShareDialog = cdb.ui.common.Dialog.extend({ - - tagName: 'div', - className: 'cartodb-share-dialog', - - events: { - 'click .ok': '_ok', - 'click .cancel': '_cancel', - 'click .close': '_cancel', - "click": '_stopPropagation', - "dblclick": '_stopPropagation', - "mousedown": '_stopPropagation' - }, - - default_options: { - title: '', - description: '', - ok_title: 'Ok', - cancel_title: 'Cancel', - width: 300, - height: 200, - clean_on_hide: false, - enter_to_confirm: false, - template_name: 'common/views/dialog_base', - ok_button_classes: 'button green', - cancel_button_classes: '', - modal_type: '', - modal_class: '', - include_footer: true, - additionalButtons: [] - }, - - initialize: function() { - - _.defaults(this.options, this.default_options); - - _.bindAll(this, 'render', '_keydown'); - - this.isOpen = false; - - var self = this; - - if (this.options.target) { - this.options.target.on("click", function(e) { - e.preventDefault(); - e.stopPropagation(); - - self.open(); - - }) - } - - // Keydown bindings for the dialog - $(document).bind('keydown', this._keydown); - - // After removing the dialog, cleaning other bindings - this.bind("clean", this._reClean); - - }, - - _stopPropagation: function(ev) { - - ev.stopPropagation(); - - }, - - _stripHTML: function(input, allowed) { - - allowed = (((allowed || "") + "").toLowerCase().match(/<[a-z][a-z0-9]*>/g) || []).join(''); - - var tags = /<\/?([a-z][a-z0-9]*)\b[^>]*>/gi; - - if (!input || (typeof input != "string")) return ''; - - return input.replace(tags, function ($0, $1) { - return allowed.indexOf('<' + $1.toLowerCase() + '>') > -1 ? $0 : ''; - }); - - }, - - open: function() { - - var self = this; - - this.$el.show(0, function(){ - self.isOpen = true; - }); - - }, - - hide: function() { - - var self = this; - - this.$el.hide(0, function(){ - self.isOpen = false; - }); - - if (this.options.clean_on_hide) { - this.clean(); - } - - }, - - toggle: function() { - - if (this.isOpen) { - this.hide(); - } else { - this.open(); - } - - }, - - _truncateTitle: function(s, length) { - - return s.substr(0, length-1) + (s.length > length ? '…' : ''); - - }, - - render: function() { - - var $el = this.$el; - - var title = this.options.title; - var description = this.options.description; - var clean_description = this._stripHTML(this.options.description); - var share_url = this.options.share_url; - - var facebook_url, twitter_url; - - this.$el.addClass(this.options.size); - - var full_title = title + ": " + clean_description; - var twitter_title; - - if (title && clean_description) { - twitter_title = this._truncateTitle(title + ": " + clean_description, 112) + " %23map " - } else if (title) { - twitter_title = this._truncateTitle(title, 112) + " %23map" - } else if (clean_description){ - twitter_title = this._truncateTitle(clean_description, 112) + " %23map" - } else { - twitter_title = "%23map" - } - - if (this.options.facebook_url) { - facebook_url = this.options.facebook_url; - } else { - facebook_url = "http://www.facebook.com/sharer.php?u=" + share_url + "&text=" + full_title; - } - - if (this.options.twitter_url) { - twitter_url = this.options.twitter_url; - } else { - twitter_url = "https://twitter.com/share?url=" + share_url + "&text=" + twitter_title; - } - - var options = _.extend(this.options, { facebook_url: facebook_url, twitter_url: twitter_url }); - - $el.html(this.options.template(options)); - - $el.find(".modal").css({ - width: this.options.width - }); - - if (this.render_content) { - this.$('.content').append(this.render_content()); - } - - if(this.options.modal_class) { - this.$el.addClass(this.options.modal_class); - } - - if (this.options.disableLinks) { - this.$el.find("a").attr("target", ""); - } - - return this; - } - -}); -/** - * generic embbed notification, like twitter "new notifications" - * - * it shows slowly the notification with a message and a close button. - * Optionally you can set a timeout to close - * - * usage example: - * - var notification = new cdb.ui.common.Notificaiton({ - el: "#notification_element", - msg: "error!", - timeout: 1000 - }); - notification.show(); - // close it - notification.close(); -*/ - -cdb.ui.common.Notification = cdb.core.View.extend({ - - tagName: 'div', - className: 'dialog', - - events: { - 'click .close': 'hide' - }, - - default_options: { - timeout: 0, - msg: '', - hideMethod: '', - duration: 'normal' - }, - - initialize: function() { - this.closeTimeout = -1; - _.defaults(this.options, this.default_options); - this.template = this.options.template ? _.template(this.options.template) : cdb.templates.getTemplate('common/notification'); - - this.$el.hide(); - }, - - render: function() { - var $el = this.$el; - $el.html(this.template(this.options)); - if(this.render_content) { - this.$('.content').append(this.render_content()); - } - return this; - }, - - hide: function(ev) { - var self = this; - if (ev) - ev.preventDefault(); - clearTimeout(this.closeTimeout); - if(this.options.hideMethod != '' && this.$el.is(":visible") ) { - this.$el[this.options.hideMethod](this.options.duration, 'swing', function() { - self.$el.html(''); - self.trigger('notificationDeleted'); - self.remove(); - }); - } else { - this.$el.hide(); - self.$el.html(''); - self.trigger('notificationDeleted'); - self.remove(); - } - - }, - - open: function(method, options) { - this.render(); - this.$el.show(method, options); - if(this.options.timeout) { - this.closeTimeout = setTimeout(_.bind(this.hide, this), this.options.timeout); - } - } - -}); - -/** - * generic table - * - * this class creates a HTML table based on Table model (see below) and modify it based on model changes - * - * usage example: - * - var table = new Table({ - model: table - }); - - $('body').append(table.render().el); - - * model should be a collection of Rows - - */ - -/** - * represents a table row - */ -cdb.ui.common.Row = cdb.core.Model.extend({ -}); - -cdb.ui.common.TableData = Backbone.Collection.extend({ - model: cdb.ui.common.Row, - fetched: false, - - initialize: function() { - var self = this; - this.bind('reset', function() { - self.fetched = true; - }) - }, - - /** - * get value for row index and columnName - */ - getCell: function(index, columnName) { - var r = this.at(index); - if(!r) { - return null; - } - return r.get(columnName); - }, - - isEmpty: function() { - return this.length === 0; - } - -}); - -/** - * contains information about the table, mainly the schema - */ -cdb.ui.common.TableProperties = cdb.core.Model.extend({ - - columnNames: function() { - return _.map(this.get('schema'), function(c) { - return c[0]; - }); - }, - - columnName: function(idx) { - return this.columnNames()[idx]; - } -}); - -/** - * renders a table row - */ -cdb.ui.common.RowView = cdb.core.View.extend({ - tagName: 'tr', - - initialize: function() { - - this.model.bind('change', this.render, this); - this.model.bind('destroy', this.clean, this); - this.model.bind('remove', this.clean, this); - this.model.bind('change', this.triggerChange, this); - this.model.bind('sync', this.triggerSync, this); - this.model.bind('error', this.triggerError, this); - - this.add_related_model(this.model); - this.order = this.options.order; - }, - - triggerChange: function() { - this.trigger('changeRow'); - }, - - triggerSync: function() { - this.trigger('syncRow'); - }, - - triggerError: function() { - this.trigger('errorRow') - }, - - valueView: function(colName, value) { - return value; - }, - - render: function() { - var self = this; - var row = this.model; - - var tr = ''; - - var tdIndex = 0; - var td; - if(this.options.row_header) { - td = '
        '; - tdIndex++; - tr += td - - var attrs = this.order || _.keys(row.attributes); - var tds = ''; - var row_attrs = row.attributes; - for(var i = 0, len = attrs.length; i < len; ++i) { - var key = attrs[i]; - var value = row_attrs[key]; - if(value !== undefined) { - var td = ''; - tdIndex++; - tds += td; - } - } - tr += tds; - this.$el.html(tr).attr('id', 'row_' + row.id); - return this; - }, - - getCell: function(x) { - var childNo = x; - if(this.options.row_header) { - ++x; - } - return this.$('td:eq(' + x + ')'); - }, - - getTableView: function() { - return this.tableView; - } - -}); - -/** - * render a table - * this widget needs two data sources - * - the table model which contains information about the table (columns and so on). See TableProperties - * - the model with the data itself (TableData) - */ -cdb.ui.common.Table = cdb.core.View.extend({ - - tagName: 'table', - rowView: cdb.ui.common.RowView, - - events: { - 'click td': '_cellClick', - 'dblclick td': '_cellDblClick' - }, - - default_options: { - }, - - initialize: function() { - var self = this; - _.defaults(this.options, this.default_options); - this.dataModel = this.options.dataModel; - this.rowViews = []; - - // binding - this.setDataSource(this.dataModel); - this.model.bind('change', this.render, this); - this.model.bind('change:dataSource', this.setDataSource, this); - - // assert the rows are removed when table is removed - this.bind('clean', this.clear_rows, this); - - // prepare for cleaning - this.add_related_model(this.dataModel); - this.add_related_model(this.model); - - // we need to use custom signals to make the tableview aware of a row being deleted, - // because when you delete a point from the map view, sometimes it isn't on the dataModel - // collection, so its destroy doesn't bubble throught there. - // Also, the only non-custom way to acknowledge that a row has been correctly deleted from a server is with - // a sync, that doesn't bubble through the table - this.model.bind('removing:row', function() { - self.rowsBeingDeleted = self.rowsBeingDeleted ? self.rowsBeingDeleted +1 : 1; - self.rowDestroying(); - }); - this.model.bind('remove:row', function() { - if(self.rowsBeingDeleted > 0) { - self.rowsBeingDeleted--; - self.rowDestroyed(); - if(self.dataModel.length == 0) { - self.emptyTable(); - } - } - }); - - }, - - headerView: function(column) { - return column[0]; - }, - - setDataSource: function(dm) { - if(this.dataModel) { - this.dataModel.unbind(null, null, this); - } - this.dataModel = dm; - this.dataModel.bind('reset', this._renderRows, this); - this.dataModel.bind('error', this._renderRows, this); - this.dataModel.bind('add', this.addRow, this); - }, - - _renderHeader: function() { - var self = this; - var thead = $(""); - var tr = $(""); - if(this.options.row_header) { - tr.append($("'; - tdIndex++; - tr += td - - var attrs = this.order || _.keys(row.attributes); - var tds = ''; - var row_attrs = row.attributes; - for(var i = 0, len = attrs.length; i < len; ++i) { - var key = attrs[i]; - var value = row_attrs[key]; - if(value !== undefined) { - var td = ''; - tdIndex++; - tds += td; - } - } - tr += tds; - this.$el.html(tr).attr('id', 'row_' + row.id); - return this; - }, - - getCell: function(x) { - var childNo = x; - if(this.options.row_header) { - ++x; - } - return this.$('td:eq(' + x + ')'); - }, - - getTableView: function() { - return this.tableView; - } - -}); - -/** - * render a table - * this widget needs two data sources - * - the table model which contains information about the table (columns and so on). See TableProperties - * - the model with the data itself (TableData) - */ -cdb.ui.common.Table = cdb.core.View.extend({ - - tagName: 'table', - rowView: cdb.ui.common.RowView, - - events: { - 'click td': '_cellClick', - 'dblclick td': '_cellDblClick' - }, - - default_options: { - }, - - initialize: function() { - var self = this; - _.defaults(this.options, this.default_options); - this.dataModel = this.options.dataModel; - this.rowViews = []; - - // binding - this.setDataSource(this.dataModel); - this.model.bind('change', this.render, this); - this.model.bind('change:dataSource', this.setDataSource, this); - - // assert the rows are removed when table is removed - this.bind('clean', this.clear_rows, this); - - // prepare for cleaning - this.add_related_model(this.dataModel); - this.add_related_model(this.model); - - // we need to use custom signals to make the tableview aware of a row being deleted, - // because when you delete a point from the map view, sometimes it isn't on the dataModel - // collection, so its destroy doesn't bubble throught there. - // Also, the only non-custom way to acknowledge that a row has been correctly deleted from a server is with - // a sync, that doesn't bubble through the table - this.model.bind('removing:row', function() { - self.rowsBeingDeleted = self.rowsBeingDeleted ? self.rowsBeingDeleted +1 : 1; - self.rowDestroying(); - }); - this.model.bind('remove:row', function() { - if(self.rowsBeingDeleted > 0) { - self.rowsBeingDeleted--; - self.rowDestroyed(); - if(self.dataModel.length == 0) { - self.emptyTable(); - } - } - }); - - }, - - headerView: function(column) { - return column[0]; - }, - - setDataSource: function(dm) { - if(this.dataModel) { - this.dataModel.unbind(null, null, this); - } - this.dataModel = dm; - this.dataModel.bind('reset', this._renderRows, this); - this.dataModel.bind('error', this._renderRows, this); - this.dataModel.bind('add', this.addRow, this); - }, - - _renderHeader: function() { - var self = this; - var thead = $(""); - var tr = $(""); - if(this.options.row_header) { - tr.append($("
        '; - } else { - td = ''; - } - var v = self.valueView('', ''); - if(v.html) { - v = v[0].outerHTML; - } - td += v; - td += ''; - var v = self.valueView(key, value); - if(v.html) { - v = v[0].outerHTML; - } - td += v; - td += '
        ").append(self.headerView(['', 'header']))); - } else { - tr.append($("").append(self.headerView(['', 'header']))); - } - _(this.model.get('schema')).each(function(col) { - tr.append($("").append(self.headerView(col))); - }); - thead.append(tr); - return thead; - }, - - /** - * remove all rows - */ - clear_rows: function() { - this.$('tfoot').remove(); - this.$('tr.noRows').remove(); - - // unbind rows before cleaning them when all are gonna be removed - var rowView = null; - while(rowView = this.rowViews.pop()) { - // this is a hack to avoid all the elements are removed one by one - rowView.unbind(null, null, this); - // each element removes itself from rowViews - rowView.clean(); - } - // clean all the html at the same time - this.rowViews = []; - }, - - /** - * add rows - */ - addRow: function(row, collection, options) { - var self = this; - var tr = new self.rowView({ - model: row, - order: this.model.columnNames(), - row_header: this.options.row_header - }); - tr.tableView = this; - - tr.bind('clean', function() { - var idx = _.indexOf(self.rowViews, tr); - self.rowViews.splice(idx, 1); - // update index - for(var i = idx; i < self.rowViews.length; ++i) { - self.rowViews[i].$el.attr('data-y', i); - } - }, this); - tr.bind('changeRow', this.rowChanged, this); - tr.bind('saved', this.rowSynched, this); - tr.bind('errorSaving', this.rowFailed, this); - tr.bind('saving', this.rowSaving, this); - this.retrigger('saving', tr); - - tr.render(); - if(options && options.index !== undefined && options.index != self.rowViews.length) { - - tr.$el.insertBefore(self.rowViews[options.index].$el); - self.rowViews.splice(options.index, 0, tr); - //tr.$el.attr('data-y', options.index); - // change others view data-y attribute - for(var i = options.index; i < self.rowViews.length; ++i) { - self.rowViews[i].$el.attr('data-y', i); - } - } else { - // at the end - tr.$el.attr('data-y', self.rowViews.length); - self.$el.append(tr.el); - self.rowViews.push(tr); - } - - this.trigger('createRow'); - }, - - /** - * Callback executed when a row change - * @method rowChanged - * @abstract - */ - rowChanged: function() {}, - - /** - * Callback executed when a row is sync - * @method rowSynched - * @abstract - */ - rowSynched: function() {}, - - /** - * Callback executed when a row fails to reach the server - * @method rowFailed - * @abstract - */ - rowFailed: function() {}, - - /** - * Callback executed when a row send a POST to the server - * @abstract - */ - rowSaving: function() {}, - - /** - * Callback executed when a row is being destroyed - * @method rowDestroyed - * @abstract - */ - rowDestroying: function() {}, - - /** - * Callback executed when a row gets destroyed - * @method rowDestroyed - * @abstract - */ - rowDestroyed: function() {}, - - /** - * Callback executed when a row gets destroyed and the table data is empty - * @method emptyTable - * @abstract - */ - emptyTable: function() {}, - - /** - * Checks if the table is empty - * @method isEmptyTable - * @returns boolean - */ - isEmptyTable: function() { - return (this.dataModel.length === 0 && this.dataModel.fetched) - }, - - /** - * render only data rows - */ - _renderRows: function() { - this.clear_rows(); - if(! this.isEmptyTable()) { - if(this.dataModel.fetched) { - var self = this; - - this.dataModel.each(function(row) { - self.addRow(row); - }); - } else { - this._renderLoading(); - } - } else { - this._renderEmpty(); - } - - }, - - _renderLoading: function() { - }, - - _renderEmpty: function() { - }, - - /** - * Method for the children to redefine with the table behaviour when it has no rows. - * @method addEmptyTableInfo - * @abstract - */ - addEmptyTableInfo: function() { - // #to be overwrite by descendant classes - }, - - /** - * render table - */ - render: function() { - var self = this; - - // render header - self.$el.html(self._renderHeader()); - - // render data - self._renderRows(); - - return this; - - }, - - /** - * return jquery cell element of cell x,y - */ - getCell: function(x, y) { - if(this.options.row_header) { - ++y; - } - return this.rowViews[y].getCell(x); - }, - - _cellClick: function(e, evtName) { - evtName = evtName || 'cellClick'; - e.preventDefault(); - var cell = $(e.currentTarget || e.target); - var x = parseInt(cell.attr('data-x'), 10); - var y = parseInt(cell.parent().attr('data-y'), 10); - this.trigger(evtName, e, cell, x, y); - }, - - _cellDblClick: function(e) { - this._cellClick(e, 'cellDblClick'); - } - - -}); -/** - * Show a dropdown from the target - * - * It shows the several options of the user settings - * - * usage example: - * - * var settings = new cdb.ui.common.Dropdown({ - * el: "#settings_element", - * speedIn: 300, - * speedOut: 200 - * }); - * // show it - * settings.show(); - * // close it - * settings.close(); -*/ - -cdb.ui.common.Dropdown = cdb.core.View.extend({ - - tagName: 'div', - className: 'dropdown', - - events: { - "click ul li a" : "_fireClick" - }, - - default_options: { - width: 160, - speedIn: 150, - speedOut: 300, - vertical_position: "down", - horizontal_position: "right", - tick: "right", - vertical_offset: 0, - horizontal_offset: 0 - }, - - initialize: function() { - _.bindAll(this, "open", "hide", "_handleClick", "_keydown"); - - // Extend options - _.defaults(this.options, this.default_options); - - // Dropdown template - if (this.options.template_base) { - this.template_base = cdb.templates.getTemplate(this.options.template_base); - } else if (this.options.template) { - this.template_base = this.options.template; - } - - // Bind to target - $(this.options.target).bind({"click": this._handleClick}); - - // Bind ESC key - $(document).bind('keydown', this._keydown); - - // Is open flag - this.isOpen = false; - - }, - - render: function() { - // Render - var $el = this.$el; - $el - .html(this.template_base(this.options)) - .css({ - width: this.options.width - }) - return this; - }, - - _handleClick: function(ev) { - //Check if the dropdown is visible to hiding with the click on the target - if (ev){ - ev.preventDefault(); - ev.stopPropagation(); - } - // If visible - if (this.isOpen){ - this.hide(); - }else{ - this.open(); - } - }, - - _keydown: function(e) { - if (e.keyCode === 27) { - this.hide(); - } - }, - - hide: function() { - this.isOpen = false; - this.$el.hide(); - }, - - show: function() { - this.$el.css({ - display: "block", - opacity: 1 - }); - this.isOpen = true; - }, - - open: function(ev,target) { - // Target - var $target = target && $(target) || this.options.target; - this.options.target = $target; - - // Positionate - var targetPos = $target[this.options.position || 'offset']() - , targetWidth = $target.outerWidth() - , targetHeight = $target.outerHeight() - , elementWidth = this.$el.outerWidth() - , elementHeight = this.$el.outerHeight() - , self = this; - - this.$el.css({ - top: targetPos.top + parseInt((self.options.vertical_position == "up") ? (- elementHeight - 10 - self.options.vertical_offset) : (targetHeight + 10 - self.options.vertical_offset)), - left: targetPos.left + parseInt((self.options.horizontal_position == "left") ? (self.options.horizontal_offset - 15) : (targetWidth - elementWidth + 15 - self.options.horizontal_offset)) - }) - .addClass( - // Add vertical and horizontal position class - (this.options.vertical_position == "up" ? "vertical_top" : "vertical_bottom" ) - + " " + - (this.options.horizontal_position == "right" ? "horizontal_right" : "horizontal_left" ) - + " " + - // Add tick class - "tick_" + this.options.tick - ) - - // Show it - this.show(); - - // Dropdown openned - this.isOpen = true; - }, - - _fireClick: function(ev) { - this.trigger("optionClicked", ev, this.el); - } -}); -(function() { - -var _requestCache = {}; - -/** - * defines the container for an overlay. - * It places the overlay - */ -var Overlay = { - - _types: {}, - - // register a type to be created - register: function(type, creatorFn) { - Overlay._types[type] = creatorFn; - }, - - // create a type given the data - // raise an exception if the type does not exist - create: function(type, vis, data) { - var t = Overlay._types[type]; - - if (!t) { - cdb.log.error("Overlay: " + type + " does not exist"); - return; - } - - data.options = typeof data.options === 'string' ? JSON.parse(data.options): data.options; - data.options = data.options || {} - var widget = t(data, vis); - - if (widget) { - widget.type = type; - return widget; - } - - return false; - } -}; - -cdb.vis.Overlay = Overlay; - -// layer factory -var Layers = { - - _types: {}, - - register: function(type, creatorFn) { - this._types[type] = creatorFn; - }, - - create: function(type, vis, data) { - if (!type) { - cdb.log.error("creating a layer without type"); - return null; - } - var t = this._types[type.toLowerCase()]; - - var c = {}; - c.type = type; - _.extend(c, data, data.options); - return new t(vis, c); - }, - - moduleForLayer: function(type) { - if (type.toLowerCase() === 'torque') { - return 'torque'; - } - return null; - }, - - modulesForLayers: function(layers) { - var modules = _(layers).map(function(layer) { - return Layers.moduleForLayer(layer.type || layer.kind); - }); - return _.compact(_.uniq(modules)); - } - -}; - -cdb.vis.Layers = Layers; - -var Loader = cdb.vis.Loader = { - - queue: [], - current: undefined, - _script: null, - head: null, - - loadScript: function(src) { - var script = document.createElement('script'); - script.type = 'text/javascript'; - script.src = src; - script.async = true; - if (!Loader.head) { - Loader.head = document.getElementsByTagName('head')[0]; - } - // defer the loading because IE9 loads in the same frame the script - // so Loader._script is null - setTimeout(function() { - Loader.head.appendChild(script); - }, 0); - return script; - }, - - get: function(url, callback) { - if (!Loader._script) { - Loader.current = callback; - Loader._script = Loader.loadScript(url + (~url.indexOf('?') ? '&' : '?') + 'callback=vizjson'); - } else { - Loader.queue.push([url, callback]); - } - }, - - getPath: function(file) { - var scripts = document.getElementsByTagName('script'), - cartodbJsRe = /\/?cartodb[\-\._]?([\w\-\._]*)\.js\??/; - for (i = 0, len = scripts.length; i < len; i++) { - src = scripts[i].src; - matches = src.match(cartodbJsRe); - - if (matches) { - var bits = src.split('/'); - delete bits[bits.length - 1]; - return bits.join('/') + file; - } - } - return null; - }, - - loadModule: function(modName) { - var file = "cartodb.mod." + modName + (cartodb.DEBUG ? ".uncompressed.js" : ".js"); - var src = this.getPath(file); - if (!src) { - cartodb.log.error("can't find cartodb.js file"); - } - Loader.loadScript(src); - } -}; - -window.vizjson = function(data) { - Loader.current && Loader.current(data); - // remove script - Loader.head.removeChild(Loader._script); - Loader._script = null; - // next element - var a = Loader.queue.shift(); - if (a) { - Loader.get(a[0], a[1]); - } -}; - -cartodb.moduleLoad = function(name, mod) { - cartodb[name] = mod; - cartodb.config.modules.add({ - name: mod - }); -}; - -/** - * visulization creation - */ -var Vis = cdb.core.View.extend({ - - initialize: function() { - _.bindAll(this, 'loadingTiles', 'loadTiles', '_onResize'); - - this.https = false; - this.overlays = []; - this.moduleChecked = false; - this.layersLoading = 0; - - if (this.options.mapView) { - this.mapView = this.options.mapView; - this.map = this.mapView.map; - } - - // recalculate map position on orientation change - if (!window.addEventListener) { - window.attachEvent('orientationchange', this.doOnOrientationChange, this); - } else { - window.addEventListener('orientationchange', _.bind(this.doOnOrientationChange, this)); - } - - }, - - doOnOrientationChange: function() { - //this.setMapPosition(); - }, - - /** - * check if all the modules needed to create layers are loaded - */ - checkModules: function(layers) { - var mods = Layers.modulesForLayers(layers); - return _.every(_.map(mods, function(m) { return cartodb[m] !== undefined; })); - }, - - loadModules: function(layers, done) { - var self = this; - var mods = Layers.modulesForLayers(layers); - for(var i = 0; i < mods.length; ++i) { - Loader.loadModule(mods[i]); - } - function loaded () { - if (self.checkModules(layers)) { - cdb.config.unbind('moduleLoaded', loaded); - done(); - } - } - - cdb.config.bind('moduleLoaded', loaded); - _.defer(loaded); - }, - - - _addLayers: function(layers, options) { - for(var i = 0; i < layers.length; ++i) { - var layerData = layers[i]; - this.loadLayer(layerData, options); - } - }, - - addLegends: function(layers, mobile_enabled) { - - this.legends = new cdb.geo.ui.StackedLegend({ - legends: this.createLegendView(layers) - }); - - if (!mobile_enabled) { - this.mapView.addOverlay(this.legends); - } - - }, - - _setLayerOptions: function(options) { - - var layers = []; - - // flatten layers (except baselayer) - var layers = _.map(this.getLayers().slice(1), function(layer) { - if (layer.getSubLayers) { - return layer.getSubLayers(); - } - return layer; - }); - - layers = _.flatten(layers); - - for (i = 0; i < Math.min(options.sublayer_options.length, layers.length); ++i) { - - var o = options.sublayer_options[i]; - var subLayer = layers[i]; - var legend = this.legends && this.legends.getLegendByIndex(i); - - if (legend) { - legend[o.visible ? 'show': 'hide'](); - } - - // HACK - if(subLayer.model && subLayer.model.get('type') === 'torque') { - if (o.visible === false) { - subLayer.model.set('visible', false); - var timeSlider = this.getOverlay('time_slider'); - if (timeSlider) { - timeSlider.hide(); - } - } - } else { - if (o.visible === false) subLayer.hide(); - } - } - }, - - _addOverlays: function(overlays, options) { - - // Sort the overlays by its internal order - overlays = _.sortBy(overlays, function(overlay){ return overlay.order == null ? 1000 : overlay.order; }); - - this._createOverlays(overlays, options); - - }, - - addTimeSlider: function(torqueLayer) { - - if (torqueLayer) { - - this.addOverlay({ - type: 'time_slider', - layer: torqueLayer - }); - - } - - }, - - _setupSublayers: function(layers, options) { - - options.sublayer_options = []; - - _.each(layers.slice(1), function(lyr) { - - if (lyr.type === 'layergroup') { - _.each(lyr.options.layer_definition.layers, function(l) { - options.sublayer_options.push({ visible: ( l.visible !== undefined ? l.visible : true ) }) - }); - } else if (lyr.type === 'namedmap') { - _.each(lyr.options.named_map.layers, function(l) { - options.sublayer_options.push({ visible: ( l.visible !== undefined ? l.visible : true ) }) - }); - } else if (lyr.type === 'torque') { - options.sublayer_options.push({ visible: ( lyr.options.visible !== undefined ? lyr.options.visible : true ) }) - } - - }); - - }, - - load: function(data, options) { - var self = this; - - if (typeof(data) === 'string') { - - var url = data; - - cdb.vis.Loader.get(url, function(data) { - if (data) { - self.load(data, options); - } else { - self.throwError('error fetching viz.json file'); - } - }); - - return this; - - } - - if (!this.checkModules(data.layers)) { - - if (this.moduleChecked) { - - self.throwError("modules couldn't be loaded"); - return this; - - } - - this.moduleChecked = true; - - // load modules needed for layers - this.loadModules(data.layers, function() { - self.load(data, options); - }); - - return this; - - } - - // configure the vis in http or https - if (window && window.location.protocol && window.location.protocol === 'https:') { - this.https = true; - } - - if (data.https) { - this.https = data.https; - } - - options = options || {}; - - this._applyOptions(data, options); - - // to know if the logo is enabled search in the overlays and see if logo overlay is included and is shown - var has_logo_overlay = !!_.find(data.overlays, function(o) { return o.type === 'logo' && o.options.display; }); - - this.cartodb_logo = (options.cartodb_logo !== undefined) ? options.cartodb_logo: has_logo_overlay; - - if (this.mobile) this.cartodb_logo = false; - else if (!has_logo_overlay && options.cartodb_logo === undefined) this.cartodb_logo = true; // We set the logo by default - - var scrollwheel = (options.scrollwheel === undefined) ? data.scrollwheel : options.scrollwheel; - - // map - data.maxZoom || (data.maxZoom = 20); - data.minZoom || (data.minZoom = 0); - - //Force using GMaps ? - if ( (this.gmaps_base_type) && (data.map_provider === "leaflet") ) { - - //Check if base_type is correct - var typesAllowed = ['roadmap', 'gray_roadmap', 'dark_roadmap', 'hybrid', 'satellite', 'terrain']; - if (_.contains(typesAllowed, this.gmaps_base_type)) { - if (data.layers) { - data.layers[0].options.type = 'GMapsBase'; - data.layers[0].options.base_type = this.gmaps_base_type; - data.layers[0].options.name = this.gmaps_base_type; - - if (this.gmaps_style) { - data.layers[0].options.style = typeof this.gmaps_style === 'string' ? JSON.parse(this.gmaps_style): this.gmaps_style; - } - - data.map_provider = 'googlemaps'; - data.layers[0].options.attribution = ''; //GMaps has its own attribution - } else { - cdb.log.error('No base map loaded. Using Leaflet.'); - } - } else { - cdb.log.error('GMaps base_type "' + this.gmaps_base_type + ' is not supported. Using leaflet.'); - } - } - - var mapConfig = { - title: data.title, - description: data.description, - maxZoom: data.maxZoom, - minZoom: data.minZoom, - legends: data.legends, - scrollwheel: scrollwheel, - provider: data.map_provider - }; - - // if the boundaries are defined, we add them to the map - if (data.bounding_box_sw && data.bounding_box_ne) { - - mapConfig.bounding_box_sw = data.bounding_box_sw; - mapConfig.bounding_box_ne = data.bounding_box_ne; - - } - - if (data.bounds) { - - mapConfig.view_bounds_sw = data.bounds[0]; - mapConfig.view_bounds_ne = data.bounds[1]; - - } else { - var center = data.center; - - if (typeof(center) === "string") { - center = $.parseJSON(center); - } - - mapConfig.center = center || [0, 0]; - mapConfig.zoom = data.zoom == undefined ? 4: data.zoom; - } - - var map = new cdb.geo.Map(mapConfig); - this.map = map; - - this.updated_at = data.updated_at || new Date().getTime(); - - // If a CartoDB embed map is hidden by default, its - // height is 0 and it will need to recalculate its size - // and re-center again. - // We will wait until it is resized and then apply - // the center provided in the parameters and the - // correct size. - var map_h = this.$el.outerHeight(); - - if (map_h === 0) { - this.mapConfig = mapConfig; - $(window).bind('resize', this._onResize); - } - - var div = $('
        ').css({ - position: 'relative', - width: '100%', - height: '100%' - }); - - this.container = div; - - // Another div to prevent leaflet grabbing the div - var div_hack = $('
        ') - .addClass("cartodb-map-wrapper") - .css({ - position: "absolute", - top: 0, - left: 0, - right: 0, - bottom: 0, - width: '100%' - }); - - div.append(div_hack); - - this.$el.append(div); - - // Create the map - var mapView = new cdb.geo.MapView.create(div_hack, map); - - this.mapView = mapView; - - this._addLayers(data.layers, options); - - if (options.legends || (options.legends === undefined && this.map.get("legends") !== false)) this.addLegends(data.layers, this.mobile_enabled); - - if (options.time_slider) { - - var torque = _(this.getLayers()).filter(function(layer) { return layer.model.get('type') === 'torque'; }) - - if (torque && torque.length) { - - this.torqueLayer = torque[0]; - - if (!this.mobile_enabled && this.torqueLayer) { - - this.addTimeSlider(this.torqueLayer); - - } - } - } - - if (!options.sublayer_options) this._setupSublayers(data.layers, options); - if (options.sublayer_options) this._setLayerOptions(options); - - if (this.mobile_enabled){ - - if (options.legends === undefined) { - options.legends = this.legends ? true : false; - } - - this.addMobile(data.overlays, data.layers, options); - } - - this._addOverlays(data.overlays, options); - - _.defer(function() { - self.trigger('done', self, self.getLayers()); - }) - - return this; - - }, - - _addFullScreen: function() { - - this.addOverlay({ - options: { - allowWheelOnFullscreen: true - }, - type: 'fullscreen' - }); - - }, - - _createOverlays: function(overlays, options) { - - _.each(overlays, function(data) { - - var type = data.type; - - // We don't render certain overlays if we are in mobile - if (this.mobile_enabled && type === "zoom") return; - if (this.mobile_enabled && type === 'header') return; - - // IE<10 doesn't support the Fullscreen API - if (type === 'fullscreen' && $.browser.msie && parseFloat($.browser.version) <= 10) return; - - // Decide to create or not the custom overlays - if (type === 'image' || type === 'text' || type === 'annotation') { - - var isDevice = data.options.device == "mobile" ? true : false; - if (this.mobile !== isDevice) return; - - if (!options[type] && options[type] !== undefined) return; - - } - - // We add the overlay - var overlay = this.addOverlay(data); - - // We show/hide the overlays - if (overlay && (type in options) && options[type] === false) overlay.hide(); - - var opt = data.options; - - if (type == 'share' && options["shareable"] || type == 'share' && overlay.model.get("display") && options["shareable"] == undefined) overlay.show(); - if (type == 'layer_selector' && options[type] || type == 'layer_selector' && overlay.model.get("display") && options[type] == undefined) overlay.show(); - if (type == 'fullscreen' && options[type] || type == 'fullscreen' && overlay.model.get("display") && options[type] == undefined) overlay.show(); - - if (!this.mobile_enabled && (type == 'search' && options[type] || type == 'search' && opt.display && options[type] == undefined)) overlay.show(); - - if (!this.mobile_enabled && type === 'header') { - - var m = overlay.model; - - if (options.title !== undefined) { - m.set("show_title", options.title); - } - - if (options.description !== undefined) { - m.set("show_description", options.description); - } - - if (m.get('show_title') || m.get('show_description')) { - $(".cartodb-map-wrapper").addClass("with_header"); - } - - overlay.render() - } - - }, this); - - }, - - addMobile: function(overlays, data_layers, options) { - - var layers; - var layer = data_layers[1]; - - if (layer.options && layer.options.layer_definition) { - layers = layer.options.layer_definition.layers; - } else if (layer.options && layer.options.named_map && layer.options.named_map.layers) { - layers = layer.options.named_map.layers; - } - - this.addOverlay({ - type: 'mobile', - layers: layers, - overlays: overlays, - options: options, - torqueLayer: this.torqueLayer - }); - - }, - - createLegendView: function(layers) { - var legends = []; - for(var i = layers.length - 1; i>= 0; --i) { - var layer = layers[i]; - if(layer.legend) { - layer.legend.data = layer.legend.items; - var legend = layer.legend; - - if((legend.items && legend.items.length) || legend.template) { - layer.legend.index = i; - legends.push(new cdb.geo.ui.Legend(layer.legend)); - } - } - if(layer.options && layer.options.layer_definition) { - legends = legends.concat(this.createLegendView(layer.options.layer_definition.layers)); - } else if(layer.options && layer.options.named_map && layer.options.named_map.layers) { - legends = legends.concat(this.createLegendView(layer.options.named_map.layers)); - } - } - return legends; - }, - - addOverlay: function(overlay) { - - overlay.map = this.map; - - var v = Overlay.create(overlay.type, this, overlay); - - if (v) { - // Save tiles loader view for later - if (overlay.type == "loader") { - this.loader = v; - } - - this.mapView.addOverlay(v); - - this.overlays.push(v); - - v.bind('clean', function() { - for(var i in this.overlays) { - var o = this.overlays[i]; - if (v.cid === o.cid) { - this.overlays.splice(i, 1) - return; - } - } - }, this); - - // Set map position correctly taking into account - // header height - if (overlay.type == "header") { - //this.setMapPosition(); - } - } - return v; - }, - - // change vizjson based on options - _applyOptions: function(vizjson, opt) { - opt = opt || {}; - opt = _.defaults(opt, { - tiles_loader: true, - loaderControl: true, - infowindow: true, - tooltip: true, - time_slider: true - }); - vizjson.overlays = vizjson.overlays || []; - vizjson.layers = vizjson.layers || []; - - function search_overlay(name) { - if (!vizjson.overlays) return null; - for(var i = 0; i < vizjson.overlays.length; ++i) { - if (vizjson.overlays[i].type === name) { - return vizjson.overlays[i]; - } - } - } - - function remove_overlay(name) { - if (!vizjson.overlays) return; - for(var i = 0; i < vizjson.overlays.length; ++i) { - if (vizjson.overlays[i].type === name) { - vizjson.overlays.splice(i, 1); - return; - } - } - } - - this.infowindow = opt.infowindow; - this.tooltip = opt.tooltip; - - if (opt.https) { - this.https = true; - } - - if (opt.gmaps_base_type) { - this.gmaps_base_type = opt.gmaps_base_type; - } - - if (opt.gmaps_style) { - this.gmaps_style = opt.gmaps_style; - } - - this.mobile = /Android|webOS|iPhone|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent); - this.mobile_enabled = (opt.mobile_layout && this.mobile) || opt.force_mobile; - - if (opt.force_mobile === false || opt.force_mobile === "false") this.mobile_enabled = false; - - if (!opt.title) { - vizjson.title = null; - } - - if (!opt.description) { - vizjson.description = null; - } - - if (!opt.tiles_loader) { - remove_overlay('loader'); - } - - if (!opt.loaderControl) { - remove_overlay('loader'); - } - - if (!this.mobile_enabled && opt.search) { - if (!search_overlay('search')) { - vizjson.overlays.push({ - type: "search", - order: 3 - }); - } - } - - if ( (opt.title && vizjson.title) || (opt.description && vizjson.description) ) { - - if (!search_overlay('header')) { - vizjson.overlays.unshift({ - type: "header", - order: 1, - shareable: opt.shareable ? true: false, - url: vizjson.url, - options: { - extra: { - title: vizjson.title, - description: vizjson.description, - show_title: opt.title, - show_description: opt.description - } - } - }); - } - } - - - if (opt.layer_selector) { - if (!search_overlay('layer_selector')) { - vizjson.overlays.push({ - type: "layer_selector" - }); - } - } - - if (opt.shareable && !this.mobile_enabled) { - if (!search_overlay('share')) { - vizjson.overlays.push({ - type: "share", - order: 2, - url: vizjson.url - }); - } - } - - // We remove certain overlays in mobile devices - if (this.mobile_enabled) { - remove_overlay('logo'); - remove_overlay('share'); - } - - if (this.mobile) { - remove_overlay('zoom'); - } - - // if bounds are present zoom and center will not taken into account - var zoom = parseInt(opt.zoom); - if (!isNaN(zoom)) { - vizjson.zoom = zoom; - vizjson.bounds = null; - } - - // Center coordinates? - var center_lat = parseFloat(opt.center_lat); - var center_lon = parseFloat(opt.center_lon); - if ( !isNaN(center_lat) && !isNaN(center_lon) ) { - vizjson.center = [center_lat, center_lon]; - vizjson.bounds = null; - } - - // Center object - if (opt.center !== undefined) { - vizjson.center = opt.center; - vizjson.bounds = null; - } - - // Bounds? - var sw_lat = parseFloat(opt.sw_lat); - var sw_lon = parseFloat(opt.sw_lon); - var ne_lat = parseFloat(opt.ne_lat); - var ne_lon = parseFloat(opt.ne_lon); - - if ( !isNaN(sw_lat) && !isNaN(sw_lon) && !isNaN(ne_lat) && !isNaN(ne_lon) ) { - vizjson.bounds = [ - [ sw_lat, sw_lon ], - [ ne_lat, ne_lon ] - ]; - } - - if (vizjson.layers.length > 1) { - var token = opt.auth_token; - for(var i = 1; i < vizjson.layers.length; ++i) { - var o = vizjson.layers[i].options; - o.no_cdn = opt.no_cdn; - o.force_cors = opt.force_cors; - if(token) { - o.auth_token = token; - } - } - } - - }, - - // Set map top position taking into account header height - setMapPosition: function() { }, - - createLayer: function(layerData, opts) { - var layerModel = Layers.create(layerData.type || layerData.kind, this, layerData); - return this.mapView.createLayer(layerModel); - }, - - _getSqlApi: function(attrs) { - attrs = attrs || {}; - var port = attrs.sql_api_port - var domain = attrs.sql_api_domain + (port ? ':' + port: '') - var protocol = attrs.sql_api_protocol; - var version = 'v1'; - if (domain.indexOf('cartodb.com') !== -1) { - protocol = 'http'; - domain = "cartodb.com"; - version = 'v2'; - } - - var sql = new cartodb.SQL({ - user: attrs.user_name, - protocol: protocol, - host: domain, - version: version - }); - - return sql; - }, - - addTooltip: function(layerView) { - if(!layerView || !layerView.containTooltip || !layerView.containTooltip()) { - return; - } - for(var i = 0; i < layerView.getLayerCount(); ++i) { - var t = layerView.getTooltipData(i); - if (t) { - if (!layerView.tooltip) { - var tooltip = new cdb.geo.ui.Tooltip({ - layer: layerView, - template: t.template, - position: 'bottom|right', - vertical_offset: 10, - horizontal_offset: 4, - fields: t.fields, - omit_columns: ['cartodb_id'] - }); - layerView.tooltip = tooltip; - this.mapView.addOverlay(tooltip); - } - layerView.setInteraction(i, true); - } - } - - if (layerView.tooltip) { - layerView.bind("featureOver", function(e, latlng, pos, data, layer) { - var t = layerView.getTooltipData(layer); - if (t) { - layerView.tooltip.setTemplate(t.template); - layerView.tooltip.setFields(t.fields); - layerView.tooltip.setAlternativeNames(t.alternative_names); - layerView.tooltip.enable(); - } else { - layerView.tooltip.disable(); - } - }); - } - }, - - addInfowindow: function(layerView) { - - if(!layerView.containInfowindow || !layerView.containInfowindow()) { - return; - } - - var mapView = this.mapView; - var eventType = 'featureClick'; - var infowindow = null; - - // activate interactivity for layers with infowindows - for(var i = 0; i < layerView.getLayerCount(); ++i) { - //var interactivity = layerView.getSubLayer(i).get('interactivity'); - // if interactivity is not enabled we can't enable it - if(layerView.getInfowindowData(i)) {// && interactivity && interactivity.indexOf('cartodb_id') !== -1) { - if(!infowindow) { - infowindow = Overlay.create('infowindow', this, layerView.getInfowindowData(i), true); - mapView.addInfowindow(infowindow); - } - var index = layerView.getLayerNumberByIndex(i); - layerView.setInteraction(index, true); - } - } - - if(!infowindow) { - return; - } - - infowindow.bind('close', function() { - // when infowindow is closed remove all the filters - // for tooltips - for(var i = 0; i < layerView.getLayerCount(); ++i) { - var t = layerView.tooltip; - if (t) { - t.setFilter(null); - } - } - }) - - // if the layer has no infowindow just pass the interaction - // data to the infowindow - layerView.bind(eventType, function(e, latlng, pos, data, layer) { - - var infowindowFields = layerView.getInfowindowData(layer); - if (!infowindowFields) return; - var fields = _.pluck(infowindowFields.fields, 'name'); - var cartodb_id = data.cartodb_id; - - layerView.fetchAttributes(layer, cartodb_id, fields, function(attributes) { - - // Old viz.json doesn't contain width and maxHeight properties - // and we have to get the default values if there are not defined. - var extra = _.defaults( - { - offset: infowindowFields.offset, - width: infowindowFields.width, - maxHeight: infowindowFields.maxHeight - }, - cdb.geo.ui.InfowindowModel.prototype.defaults - ); - - infowindow.model.set({ - 'fields': infowindowFields.fields, - 'template': infowindowFields.template, - 'template_type': infowindowFields.template_type, - 'alternative_names': infowindowFields.alternative_names, - 'offset': extra.offset, - 'width': extra.width, - 'maxHeight': extra.maxHeight - }); - - if (attributes) { - infowindow.model.updateContent(attributes); - infowindow.adjustPan(); - } else { - infowindow.setError(); - } - }); - - // Show infowindow with loading state - infowindow - .setLatLng(latlng) - .setLoading() - .showInfowindow(); - - if (layerView.tooltip) { - layerView.tooltip.setFilter(function(feature) { - return feature.cartodb_id !== cartodb_id; - }).hide(); - } - }); - - var hovers = []; - - layerView.bind('mouseover', function() { - mapView.setCursor('pointer'); - }); - - layerView.bind('mouseout', function(m, layer) { - mapView.setCursor('auto'); - }); - - layerView.infowindow = infowindow.model; - }, - - loadLayer: function(layerData, opts) { - var map = this.map; - var mapView = this.mapView; - //layerData.type = layerData.kind; - var layer_cid = map.addLayer(Layers.create(layerData.type || layerData.kind, this, layerData), opts); - - var layerView = mapView.getLayerByCid(layer_cid); - - if (!layerView) { - this.throwError("layer can't be created", map.layers.getByCid(layer_cid)); - return; - } - - // add the associated overlays - if(layerView && this.infowindow && layerView.containInfowindow && layerView.containInfowindow()) { - this.addInfowindow(layerView); - } - - if(layerView && this.tooltip && layerView.containTooltip && layerView.containTooltip()) { - this.addTooltip(layerView); - } - - if (layerView) { - var self = this; - - var loadingTiles = function() { - self.loadingTiles(opts); - }; - - var loadTiles = function() { - self.loadTiles(opts); - }; - - layerView.bind('loading', loadingTiles); - layerView.bind('load', loadTiles); - } - - return layerView; - - }, - - loadingTiles: function() { - if (this.loader) { - this.loader.show() - } - if(this.layersLoading === 0) { - this.trigger('loading'); - } - this.layersLoading++; - }, - - loadTiles: function() { - if (this.loader) { - this.loader.hide(); - } - this.layersLoading--; - // check less than 0 because loading event sometimes is - // thrown before visualization creation - if(this.layersLoading <= 0) { - this.layersLoading = 0; - this.trigger('load'); - } - }, - - throwError: function(msg, lyr) { - cdb.log.error(msg); - var self = this; - _.defer(function() { - self.trigger('error', msg, lyr); - }); - }, - - error: function(fn) { - return this.bind('error', fn); - }, - - done: function(fn) { - return this.bind('done', fn); - }, - - // public methods - // - - // get the native map used behind the scenes - getNativeMap: function() { - return this.mapView.getNativeMap(); - }, - - // returns an array of layers - getLayers: function() { - var self = this; - return _.compact(this.map.layers.map(function(layer) { - return self.mapView.getLayerByCid(layer.cid); - })); - }, - - getOverlays: function() { - return this.overlays; - }, - - getOverlay: function(type) { - return _(this.overlays).find(function(v) { - return v.type == type; - }); - }, - - getOverlaysByType: function(type) { - return _(this.overlays).filter(function(v) { - return v.type == type; - }); - }, - - _onResize: function() { - - $(window).unbind('resize', this._onResize); - - var self = this; - - self.mapView.invalidateSize(); - - // This timeout is necessary due to GMaps needs time - // to load tiles and recalculate its bounds :S - setTimeout(function() { - - var c = self.mapConfig; - - if (c.view_bounds_sw) { - - self.mapView.map.setBounds([ - c.view_bounds_sw, - c.view_bounds_ne - ]); - - } else { - - self.mapView.map.set({ - center: c.center, - zoom: c.zoom - }); - - } - }, 150); - } - -}, { - - /** - * adds an infowindow to the map controlled by layer events. - * it enables interaction and overrides the layer interacivity - * ``fields`` array of column names - * ``map`` native map object, leaflet of gmaps - * ``layer`` cartodb layer (or sublayer) - */ - addInfowindow: function(map, layer, fields, opts) { - var options = _.defaults(opts || {}, { - infowindowTemplate: cdb.vis.INFOWINDOW_TEMPLATE.light, - templateType: 'mustache', - triggerEvent: 'featureClick', - templateName: 'light', - extraFields: [], - cursorInteraction: true - }); - - if(!map) throw new Error('map is not valid'); - if(!layer) throw new Error('layer is not valid'); - if(!fields && fields.length === undefined ) throw new Error('fields should be a list of strings'); - - var f = []; - fields = fields.concat(options.extraFields); - for(var i = 0; i < fields.length; ++i) { - f.push({ name: fields, order: i}); - } - - var infowindowModel = new cdb.geo.ui.InfowindowModel({ - fields: f, - template_name: options.templateName - }); - - var infowindow = new cdb.geo.ui.Infowindow({ - model: infowindowModel, - mapView: map.viz.mapView, - template: new cdb.core.Template({ - template: options.infowindowTemplate, - type: options.templateType - }).asFunction() - }); - - map.viz.mapView.addInfowindow(infowindow); - // try to change interactivity, it the layer is a named map - // it's inmutable so it'a assumed the interactivity already has - // the fields it needs - try { - layer.setInteractivity(fields); - } catch(e) { - } - layer.setInteraction(true); - - layer.bind(options.triggerEvent, function(e, latlng, pos, data, layer) { - var render_fields = []; - var d; - for (var f = 0; f < fields.length; ++f) { - var field = fields[f]; - if (d = data[field]) { - render_fields.push({ - title: field, - value: d, - index: 0 - }); - } - } - - infowindow.model.set({ - content: { - fields: render_fields, - data: data - } - }); - - infowindow - .setLatLng(latlng) - .showInfowindow(); - infowindow.adjustPan(); - }, infowindow); - - // remove the callback on clean - infowindow.bind('clean', function() { - layer.unbind(options.triggerEvent, null, infowindow); - }); - - if(options.cursorInteraction) { - cdb.vis.Vis.addCursorInteraction(map, layer); - } - - return infowindow; - - }, - - addCursorInteraction: function(map, layer) { - var mapView = map.viz.mapView; - layer.bind('mouseover', function() { - mapView.setCursor('pointer'); - }); - - layer.bind('mouseout', function(m, layer) { - mapView.setCursor('auto'); - }); - }, - - removeCursorInteraction: function(map, layer) { - var mapView = map.viz.mapView; - layer.unbind(null, null, mapView); - } - -}); - -cdb.vis.INFOWINDOW_TEMPLATE = { - light: [ - '
        ', - 'x', - '
        ', - '
        ', - '{{#content.fields}}', - '{{#title}}

        {{title}}

        {{/title}}', - '{{#value}}', - '

        {{{ value }}}

        ', - '{{/value}}', - '{{^value}}', - '

        null

        ', - '{{/value}}', - '{{/content.fields}}', - '
        ', - '
        ', - '
        ', - '
        ' - ].join('') -}; - -cdb.vis.Vis = Vis; - -})(); -(function() { - -cdb.vis.Overlay.register('logo', function(data, vis) { - -}); - -cdb.vis.Overlay.register('mobile', function(data, vis) { - - var template = cdb.core.Template.compile( - data.template || '\ -
        \ -
        \ -
        \ - \ - \ -
        \ -
        \ -
        \ -
        \ -
        \ -
          \ -
          \ -
          \ -
          \ - \ -
          \ - ', - data.templateType || 'mustache' - ); - - var mobile = new cdb.geo.ui.Mobile({ - template: template, - mapView: vis.mapView, - overlays: data.overlays, - layerView: data.layerView, - visibility_options: data.options, - torqueLayer: data.torqueLayer, - map: data.map - }); - - return mobile.render(); -}); - -cdb.vis.Overlay.register('image', function(data, vis) { - - var options = data.options; - - var template = cdb.core.Template.compile( - data.template || '\ -
          \ -
          {{{ content }}}
          \ -
          ', - data.templateType || 'mustache' - ); - - var widget = new cdb.geo.ui.Image({ - model: new cdb.core.Model(options), - template: template - }); - - return widget.render(); - -}); - -cdb.vis.Overlay.register('text', function(data, vis) { - - var options = data.options; - - var template = cdb.core.Template.compile( - data.template || '\ -
          \ -
          {{{ text }}}
          \ -
          ', - data.templateType || 'mustache' - ); - - var widget = new cdb.geo.ui.Text({ - model: new cdb.core.Model(options), - template: template, - className: "cartodb-overlay overlay-text " + options.device - }); - - return widget.render(); - -}); - -cdb.vis.Overlay.register('annotation', function(data, vis) { - - var options = data.options; - - var template = cdb.core.Template.compile( - data.template || '\ -
          \ -
          {{{ text }}}
          \ -
          \ -
          ', - data.templateType || 'mustache' - ); - - var options = data.options; - - var widget = new cdb.geo.ui.Annotation({ - className: "cartodb-overlay overlay-annotation " + options.device, - template: template, - mapView: vis.mapView, - device: options.device, - text: options.extra.rendered_text, - minZoom: options.style["min-zoom"], - maxZoom: options.style["max-zoom"], - latlng: options.extra.latlng, - style: options.style - }); - - return widget.render(); - -}); - - -cdb.vis.Overlay.register('zoom_info', function(data, vis) { - //console.log("placeholder for the zoom_info overlay"); -}); - -cdb.vis.Overlay.register('header', function(data, vis) { - - var options = data.options; - - var template = cdb.core.Template.compile( - data.template || '\ -
          \ -
          {{{ title }}}
          \ -
          {{{ description }}}
          \ -
          ', - data.templateType || 'mustache' - ); - - var widget = new cdb.geo.ui.Header({ - model: new cdb.core.Model(options), - template: template - }); - - return widget.render(); - -}); - -// map zoom control -cdb.vis.Overlay.register('zoom', function(data, vis) { - - if(!data.template) { - vis.trigger('error', 'zoom template is empty') - return; - } - - var zoom = new cdb.geo.ui.Zoom({ - model: data.map, - template: cdb.core.Template.compile(data.template) - }); - - return zoom.render(); - -}); - -// Tiles loader -cdb.vis.Overlay.register('loader', function(data) { - - var tilesLoader = new cdb.geo.ui.TilesLoader({ - template: cdb.core.Template.compile(data.template) - }); - - return tilesLoader.render(); -}); - -cdb.vis.Overlay.register('time_slider', function(data, viz) { - var slider = new cdb.geo.ui.TimeSlider(data); - return slider.render(); -}); - - -// Header to show informtion (title and description) -cdb.vis.Overlay.register('_header', function(data, vis) { - var MAX_SHORT_DESCRIPTION_LENGTH = 100; - - // Add the complete url for facebook and twitter - if (location.href) { - data.share_url = encodeURIComponent(location.href); - } else { - data.share_url = data.url; - } - - var template = cdb.core.Template.compile( - data.template || "\ - {{#title}}\ -

          \ - {{#url}}\ - {{title}}\ - {{/url}}\ - {{^url}}\ - {{title}}\ - {{/url}}\ -

          \ - {{/title}}\ - {{#description}}

          {{{description}}}

          {{/description}}\ - {{#mobile_shareable}}\ - \ - {{/mobile_shareable}}\ - ", - data.templateType || 'mustache' - ); - - function truncate(s, length) { - return s.substr(0, length-1) + (s.length > length ? '…' : ''); - } - - var title = data.map.get('title'); - var description = data.map.get('description'); - - var facebook_title = title + ": " + description; - var twitter_title; - - if (title && description) { - twitter_title = truncate(title + ": " + description, 112) + " %23map " - } else if (title) { - twitter_title = truncate(title, 112) + " %23map" - } else if (description){ - twitter_title = truncate(description, 112) + " %23map" - } else { - twitter_title = "%23map" - } - - var shareable = (data.shareable == "false" || !data.shareable) ? null : data.shareable; - var mobile_shareable = shareable; - - mobile_shareable = mobile_shareable && (/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)); - - var header = new cdb.geo.ui.Header({ - title: title, - description: description, - facebook_title: facebook_title, - twitter_title: twitter_title, - url: data.url, - share_url: data.share_url, - mobile_shareable: mobile_shareable, - shareable: shareable && !mobile_shareable, - template: template - }); - - return header.render(); -}); - -// infowindow -cdb.vis.Overlay.register('infowindow', function(data, vis) { - - if (_.size(data.fields) == 0) { - return null; - } - - var infowindowModel = new cdb.geo.ui.InfowindowModel({ - template: data.template, - alternative_names: data.alternative_names, - fields: data.fields, - template_name: data.template_name - }); - - var templateType = data.templateType || 'mustache'; - - var infowindow = new cdb.geo.ui.Infowindow({ - model: infowindowModel, - mapView: vis.mapView, - template: new cdb.core.Template({ template: data.template, type: templateType}).asFunction() - }); - - return infowindow; -}); - - -// layer_selector -cdb.vis.Overlay.register('layer_selector', function(data, vis) { - - var options = data.options; - //if (!options.display) return; - - var template = cdb.core.Template.compile( - data.template || '\ - Visible layers
          \ - ', - data.templateType || 'underscore' - ); - - var dropdown_template = cdb.core.Template.compile( - data.template || '\ -
            \ - ', - data.templateType || 'underscore' - ); - - var layerSelector = new cdb.geo.ui.LayerSelector({ - model: new cdb.core.Model(options), - mapView: vis.mapView, - template: template, - dropdown_template: dropdown_template, - layer_names: data.layer_names - }); - - if (vis.legends) { - - layerSelector.bind('change:visible', function(visible, order, layer) { - - if (layer.get('type') === 'torque') { - - var timeSlider = vis.getOverlay('time_slider'); - - if (timeSlider) { - timeSlider[visible ? 'show': 'hide'](); - } - - } - - if (layer.get('type') === 'layergroup' || layer.get('type') === 'torque') { - - var legend = vis.legends && vis.legends.getLegendByIndex(order); - - if (legend) { - legend[visible ? 'show': 'hide'](); - } - - } - - }); - } - - return layerSelector.render(); - -}); - -// fullscreen -cdb.vis.Overlay.register('fullscreen', function(data, vis) { - - var options = data.options; - - options.allowWheelOnFullscreen = false; - - var template = cdb.core.Template.compile( - data.template || '', - data.templateType || 'mustache' - ); - - var fullscreen = new cdb.ui.common.FullScreen({ - doc: "#map > div", - model: new cdb.core.Model(options), - mapView: vis.mapView, - template: template - }); - - return fullscreen.render(); - -}); - -// share content -cdb.vis.Overlay.register('share', function(data, vis) { - - var options = data.options; - - var template = cdb.core.Template.compile( - data.template || '', - data.templateType || 'mustache' - ); - - var widget = new cdb.geo.ui.Share({ - model: new cdb.core.Model(options), - vis: vis, - map: vis.map, - template: template - }); - - widget.createDialog(); - - return widget.render(); - -}); - -// search content -cdb.vis.Overlay.register('search', function(data, vis) { - - var options = data.options; - - //if (!options.display) return; - - var template = cdb.core.Template.compile( - data.template || '\ -
            \ - \ - \ - \ -
            \ - ', - data.templateType || 'mustache' - ); - - var search = new cdb.geo.ui.Search({ - template: template, - model: vis.map - }); - - return search.render(); - -}); - -// tooltip -cdb.vis.Overlay.register('tooltip', function(data, vis) { - var layer; - if (!data.layer) { - var layers = vis.getLayers(); - if(layers.length > 1) { - layer = layers[1]; - } - data.layer = layer; - } - - if (!data.layer) { - throw new Error("layer is null"); - } - - data.layer.setInteraction(true); - var tooltip = new cdb.geo.ui.Tooltip(data); - return tooltip; - -}); - -cdb.vis.Overlay.register('infobox', function(data, vis) { - var layer; - var layers = vis.getLayers(); - if (!data.layer) { - if(layers.length > 1) { - layer = layers[1]; - } - data.layer = layer; - } - if(!data.layer) { - throw new Error("layer is null"); - } - data.layer.setInteraction(true); - var infobox = new cdb.geo.ui.InfoBox(data); - return infobox; - -}); - -})(); - -(function() { - -var Layers = cdb.vis.Layers; - -/* - * if we are using http and the tiles of base map need to be fetched from - * https try to fix it - */ - -var HTTPS_TO_HTTP = { - 'https://dnv9my2eseobd.cloudfront.net/': 'http://a.tiles.mapbox.com/', - 'https://maps.nlp.nokia.com/': 'http://maps.nlp.nokia.com/', - 'https://tile.stamen.com/': 'http://tile.stamen.com/', - "https://{s}.maps.nlp.nokia.com/": "http://{s}.maps.nlp.nokia.com/", - "https://cartocdn_{s}.global.ssl.fastly.net/": "http://{s}.api.cartocdn.com/" -}; - -function transformToHTTP(tilesTemplate) { - for(var url in HTTPS_TO_HTTP) { - if(tilesTemplate.indexOf(url) !== -1) { - return tilesTemplate.replace(url, HTTPS_TO_HTTP[url]) - } - } - return tilesTemplate; -} - -Layers.register('tilejson', function(vis, data) { - var url = data.tiles[0]; - url = vis.https ? url: transformToHTTP(url); - return new cdb.geo.TileLayer({ - urlTemplate: url - }); -}); - -Layers.register('tiled', function(vis, data) { - var url = data.urlTemplate; - url = vis.https ? url: transformToHTTP(url); - data.urlTemplate = url; - return new cdb.geo.TileLayer(data); -}); - -Layers.register('wms', function(vis, data) { - return new cdb.geo.WMSLayer(data); -}); - -Layers.register('gmapsbase', function(vis, data) { - return new cdb.geo.GMapsBaseLayer(data); -}); - -Layers.register('plain', function(vis, data) { - return new cdb.geo.PlainLayer(data); -}); - -Layers.register('background', function(vis, data) { - return new cdb.geo.PlainLayer(data); -}); - - -function normalizeOptions(vis, data) { - if(data.infowindow && data.infowindow.fields) { - if(data.interactivity) { - if(data.interactivity.indexOf('cartodb_id') === -1) { - data.interactivity = data.interactivity + ",cartodb_id"; - } - } else { - data.interactivity = 'cartodb_id'; - } - } - // if https is forced - if(vis.https) { - data.tiler_protocol = 'https'; - data.tiler_port = 443; - data.sql_api_protocol = 'https'; - data.sql_api_port = 443; - } - data.cartodb_logo = vis.cartodb_logo == undefined ? data.cartodb_logo : vis.cartodb_logo; -} - -var cartoLayer = function(vis, data) { - normalizeOptions(vis, data); - // if sublayers are included that means a layergroup should - // be created - if(data.sublayers) { - data.type = 'layergroup'; - return new cdb.geo.CartoDBGroupLayer(data); - } - return new cdb.geo.CartoDBLayer(data); -}; - -Layers.register('cartodb', cartoLayer); -Layers.register('carto', cartoLayer); - -Layers.register('layergroup', function(vis, data) { - normalizeOptions(vis, data); - return new cdb.geo.CartoDBGroupLayer(data); -}); - -Layers.register('namedmap', function(vis, data) { - normalizeOptions(vis, data); - return new cdb.geo.CartoDBNamedMapLayer(data); -}); - -Layers.register('torque', function(vis, data) { - // default is https - if(vis.https) { - if(data.sql_api_domain && data.sql_api_domain.indexOf('cartodb.com') !== -1) { - data.sql_api_protocol = 'https'; - data.sql_api_port = 443; - data.tiler_protocol = 'https'; - data.tiler_port = 443; - } - } - data.cartodb_logo = vis.cartodb_logo == undefined ? data.cartodb_logo : vis.cartodb_logo; - return new cdb.geo.TorqueLayer(data); -}); - -})(); -/** - * public api for cartodb - */ - -(function() { - - - function _Promise() { - - } - _.extend(_Promise.prototype, Backbone.Events, { - done: function(fn) { - return this.bind('done', fn); - }, - error: function(fn) { - return this.bind('error', fn); - } - }); - - cdb._Promise = _Promise; - - var _requestCache = {}; - - /** - * compose cartodb url - */ - function cartodbUrl(opts) { - var host = opts.host || 'cartodb.com'; - var protocol = opts.protocol || 'https'; - return protocol + '://' + opts.user + '.' + host + '/api/v1/viz/' + opts.table + '/viz.json'; - } - - /** - * given layer params fetchs the layer json - */ - function _getLayerJson(layer, callback) { - var url = null; - if(layer.layers !== undefined || ((layer.kind || layer.type) !== undefined)) { - // layer object contains the layer data - _.defer(function() { callback(layer); }); - return; - } else if(layer.table !== undefined && layer.user !== undefined) { - // layer object points to cartodbjson - url = cartodbUrl(layer); - } else if(layer.indexOf && layer.indexOf('http') === 0) { - // fetch from url - url = layer; - } - if(url) { - cdb.vis.Loader.get(url, callback); - } else { - _.defer(function() { callback(null); }); - } - } - - /** - * create a layer for the specified map - * - * @param map should be a L.Map or google.maps.Map object - * @param layer should be an url or a javascript object with the data to create the layer - * @param options layer options - * - */ - - cartodb.createLayer = function(map, layer, options, callback) { - - var promise = new _Promise(); - var layerView, MapType; - options = options || {}; - if(map === undefined) { - throw new TypeError("map should be provided"); - } - if(layer === undefined) { - throw new TypeError("layer should be provided"); - } - var args = arguments, - fn = args[args.length -1]; - if(_.isFunction(fn)) { - callback = fn; - } - - promise.addTo = function(map, position) { - promise.on('done', function() { - MapType.addLayerToMap(layerView, map, position); - }); - return promise; - }; - - _getLayerJson(layer, function(visData) { - - var layerData; - - if(!visData) { - promise.trigger('error'); - return; - } - // extract layer data from visualization data - if(visData.layers) { - if(visData.layers.length < 2) { - promise.trigger('error', "visualization file does not contain layer info"); - } - var idx = options.layerIndex === undefined ? 1: options.layerIndex; - if(visData.layers.length <= idx) { - promise.trigger('error', 'layerIndex out of bounds'); - return; - } - layerData = visData.layers[idx]; - } else { - layerData = visData; - } - - if(!layerData) { - promise.trigger('error'); - return; - } - - - // update options - if(options && !_.isFunction(options)) { - layerData.options = layerData.options || {}; - _.extend(layerData.options, options); - } - - options = _.defaults(options, { - infowindow: true, - https: false, - legends: true, - time_slider: true, - tooltip: true - }); - - // check map type - // TODO: improve checking - if(typeof(map.overlayMapTypes) !== "undefined") { - MapType = cdb.geo.GoogleMapsMapView; - // check if leaflet is loaded globally - } else if(map instanceof L.Map || (window.L && map instanceof window.L.Map)) { - MapType = cdb.geo.LeafletMapView; - } else { - promise.trigger('error', "cartodb.js can't guess the map type"); - return promise; - } - - // create a dummy viz - var viz = map.viz; - if(!viz) { - var mapView = new MapType({ - map_object: map, - map: new cdb.geo.Map() - }); - - map.viz = viz = new cdb.vis.Vis({ - mapView: mapView - }); - - viz.updated_at = visData.updated_at; - viz.https = options.https; - } - - function createLayer() { - layerView = viz.createLayer(layerData, { no_base_layer: true }); - - var torqueLayer; - var mobileEnabled = /Android|webOS|iPhone|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent); - var addMobileLayout = (options.mobile_layout && mobileEnabled) || options.force_mobile; - - if(!layerView) { - promise.trigger('error', "layer not supported"); - return promise; - } - if(options.infowindow) { - viz.addInfowindow(layerView); - } - if(options.tooltip) { - viz.addTooltip(layerView); - } - if(options.legends) { - viz.addLegends([layerData], ((mobileEnabled && options.mobile_layout) || options.force_mobile)); - } - - if(options.time_slider && layerView.model.get('type') === 'torque') { - - if (!addMobileLayout) { // don't add the overlay if we are in mobile - viz.addTimeSlider(layerView); - } - - torqueLayer = layerView; - } - - if (addMobileLayout) { - - options.mapView = map.viz.mapView; - - viz.addOverlay({ - type: 'mobile', - layerView: layerView, - overlays: [], - torqueLayer: torqueLayer, - options: options - }); - } - - callback && callback(layerView); - promise.trigger('done', layerView); - } - - // load needed modules - if(!viz.checkModules([layerData])) { - viz.loadModules([layerData], function() { - createLayer(); - }); - } else { - createLayer(); - } - - }); - - return promise; - - }; - - -})(); - -;(function() { - - var root = this; - - root.cartodb = root.cartodb || {}; - - function SQL(options) { - if(cartodb === this || window === this) { - return new SQL(options); - } - if(!options.user) { - throw new Error("user should be provided"); - } - var loc = new String(window.location.protocol); - loc = loc.slice(0, loc.length - 1); - if(loc == 'file') { - loc = 'https'; - } - - this.ajax = options.ajax || (typeof(jQuery) !== 'undefined' ? jQuery.ajax: reqwest); - if(!this.ajax) { - throw new Error("jQuery or reqwest should be loaded"); - } - - this.options = _.defaults(options, { - version: 'v2', - protocol: loc, - jsonp: typeof(jQuery) !== 'undefined' ? !jQuery.support.cors: false - }) - } - - SQL.prototype._host = function() { - var opts = this.options; - if(opts && opts.completeDomain) { - return opts.completeDomain + '/api/' + opts.version + '/sql' - } else { - var host = opts.host || 'cartodb.com'; - var protocol = opts.protocol || 'https'; - - return protocol + '://' + opts.user + '.' + host + '/api/' + opts.version + '/sql'; - } - } - - /** - * var sql = new SQL('cartodb_username'); - * sql.execute("select * form {table} where id = {id}", { - * table: 'test', - * id: '1' - * }) - */ - SQL.prototype.execute = function(sql, vars, options, callback) { - - //Variable that defines if a query should be using get method or post method - var MAX_LENGTH_GET_QUERY = 1024; - - var promise = new cartodb._Promise(); - if(!sql) { - throw new TypeError("sql should not be null"); - } - // setup arguments - var args = arguments, - fn = args[args.length -1]; - if(_.isFunction(fn)) { - callback = fn; - } - options = _.defaults(options || {}, this.options); - var params = { - type: 'get', - dataType: 'json', - crossDomain: true - }; - - if(options.cache !== undefined) { - params.cache = options.cache; - } - - if(options.jsonp) { - delete params.crossDomain; - if (options.jsonpCallback) { - params.jsonpCallback = options.jsonpCallback; - } - params.dataType = 'jsonp'; - } - - // Substitute mapnik tokens - // resolution at zoom level 0 - var res = '156543.03515625'; - // full webmercator extent - var ext = 'ST_MakeEnvelope(-20037508.5,-20037508.5,20037508.5,20037508.5,3857)'; - sql = sql.replace('!bbox!', ext) - .replace('!pixel_width!', res) - .replace('!pixel_height!', res); - - // create query - var query = Mustache.render(sql, vars); - - // check method: if we are going to send by get or by post - var isGetRequest = query.length < MAX_LENGTH_GET_QUERY; - - // generate url depending on the http method - var reqParams = ['format', 'dp', 'api_key']; - // request params - if (options.extra_params) { - reqParams = reqParams.concat(options.extra_params); - } - - params.url = this._host() ; - if (isGetRequest) { - var q = 'q=' + encodeURIComponent(query); - for(var i in reqParams) { - var r = reqParams[i]; - var v = options[r]; - if(v) { - q += '&' + r + "=" + v; - } - } - - params.url += '?' + q; - } else { - var objPost = {'q': query}; - for(var i in reqParams) { - var r = reqParams[i]; - var v = options[r]; - if (v) { - objPost[r] = v; - } - } - - params.data = objPost; - //Check if we are using jQuery(uncompressed) or reqwest (core) - if ((typeof(jQuery) !== 'undefined')) { - params.type = 'post'; - } else { - params.method = 'post'; - } - } - - // wrap success and error functions - var success = options.success; - var error = options.error; - if(success) delete options.success; - if(error) delete error.success; - - params.error = function(resp) { - var res = resp.responseText || resp.response; - var errors = res && JSON.parse(res); - promise.trigger('error', errors && errors.error, resp) - if(error) error(resp); - } - params.success = function(resp, status, xhr) { - // manage rewest - if(status == undefined) { - status = resp.status; - xhr = resp; - resp = JSON.parse(resp.response); - } - promise.trigger('done', resp, status, xhr); - if(success) success(resp, status, xhr); - if(callback) callback(resp); - } - - // call ajax - delete options.jsonp; - this.ajax(_.extend(params, options)); - return promise; - } - - SQL.prototype.getBounds = function(sql, vars, options, callback) { - var promise = new cartodb._Promise(); - var args = arguments, - fn = args[args.length -1]; - if(_.isFunction(fn)) { - callback = fn; - } - var s = 'SELECT ST_XMin(ST_Extent(the_geom)) as minx,' + - ' ST_YMin(ST_Extent(the_geom)) as miny,'+ - ' ST_XMax(ST_Extent(the_geom)) as maxx,' + - ' ST_YMax(ST_Extent(the_geom)) as maxy' + - ' from ({{{ sql }}}) as subq'; - sql = Mustache.render(sql, vars); - this.execute(s, { sql: sql }, options) - .done(function(result) { - if (result.rows && result.rows.length > 0 && result.rows[0].maxx != null) { - var c = result.rows[0]; - var minlat = -85.0511; - var maxlat = 85.0511; - var minlon = -179; - var maxlon = 179; - - var clamp = function(x, min, max) { - return x < min ? min : x > max ? max : x; - } - - var lon0 = clamp(c.maxx, minlon, maxlon); - var lon1 = clamp(c.minx, minlon, maxlon); - var lat0 = clamp(c.maxy, minlat, maxlat); - var lat1 = clamp(c.miny, minlat, maxlat); - - var bounds = [[lat0, lon0], [lat1, lon1]]; - promise.trigger('done', bounds); - callback && callback(bounds); - } - }) - .error(function(err) { - promise.trigger('error', err); - }) - - return promise; - - } - - /** - * var people_under_10 = sql - * .table('test') - * .columns(['age', 'column2']) - * .filter('age < 10') - * .limit(15) - * .order_by('age') - * - * people_under_10(function(results) { - * }) - */ - - SQL.prototype.table = function(name) { - - var _name = name; - var _filters; - var _columns = []; - var _limit; - var _order; - var _orderDir; - var _sql = this; - - function _table() { - _table.fetch.apply(_table, arguments); - } - - _table.fetch = function(vars) { - vars = vars || {} - var args = arguments, - fn = args[args.length -1]; - if(_.isFunction(fn)) { - callback = fn; - if(args.length === 1) vars = {}; - } - _sql.execute(_table.sql(), vars, callback); - } - - _table.sql = function() { - var s = "select" - if(_columns.length) { - s += ' ' + _columns.join(',') + ' ' - } else { - s += ' * ' - } - - s += "from " + _name; - - if(_filters) { - s += " where " + _filters; - } - if(_limit) { - s += " limit " + _limit; - } - if(_order) { - s += " order by " + _order; - } - if(_orderDir) { - s += ' ' + _orderDir; - } - - return s; - } - - _table.filter = function(f) { - _filters = f; - return _table; - } - - _table.order_by= function(o) { - _order = o; - return _table; - } - _table.asc = function() { - _orderDir = 'asc' - return _table; - } - - _table.desc = function() { - _orderDir = 'desc' - return _table; - } - - _table.columns = function(c) { - _columns = c; - return _table; - } - - _table.limit = function(l) { - _limit = l; - return _table; - } - - return _table; - - } - - /* - * sql.filter(sql.f().distance('< 10km') - */ - /*cartodb.SQL.geoFilter = function() { - var _sql; - function f() {} - - f.distance = function(qty) { - qty.replace('km', '*1000') - _sql += 'st_distance(the_geom) ' + qty - } - f.or = function() { - } - - f.and = function() { - } - return f; - } - */ - - root.cartodb.SQL = SQL; - -})(); -(function() { - - cartodb.createVis = function(el, vizjson, options, callback) { - - if (!el) { - throw new TypeError("a DOM element should be provided"); - } - - var - args = arguments, - fn = args[args.length -1]; - - if (_.isFunction(fn)) { - callback = fn; - } - - el = (typeof el === 'string' ? document.getElementById(el) : el); - - var vis = new cartodb.vis.Vis({ el: el }); - - if (vizjson) { - - vis.load(vizjson, options); - - if (callback) { - vis.done(callback); - } - - } - - return vis; - - }; - -})(); -/** - * tabbed pane. - * if contains n views inside it and shows only one at once - * - * usage: - * - * var pane = new cdb.ui.common.TabPane({ - * el: $("#container") - * }); - * - * pane.addTab('tab1', new OtherView()); - * pane.addTab('tab2', new OtherView2()); - * pane.addTab('tab3', new OtherView3()); - * - * pane.active('tab1'); - * - * pane.bind('tabEnabled', function(tabName, tabView) { - * pane.bind('tabDisabled', function(tabName, tabView) { - * }); - */ -cdb.ui.common.TabPane = cdb.core.View.extend({ - - initialize: function() { - this.tabs = {}; - this.activeTab = null; - this.activePane = null; - }, - - addTab: function(name, view, options) { - options = options || { active: true }; - if(this.tabs[name] !== undefined) { - cdb.log.debug(name + "already added"); - } else { - this.tabs[name] = view.cid; - this.addView(view); - if(options.after !== undefined) { - var e = this.$el.children()[options.after]; - view.$el.insertAfter(e); - } else if(options.prepend) { - this.$el.prepend(view.el); - } else { - this.$el.append(view.el); - } - this.trigger('tabAdded', name, view); - if(options.active) { - this.active(name); - } else { - view.hide(); - } - } - }, - - getPreviousPane: function() { - var tabs = _.toArray(this.tabs); - var panes = _.toArray(this._subviews); - - var i = _.indexOf(tabs, this.activePane.cid) - 1; - if (i < 0) i = panes.length - 1; - - return panes[i]; - }, - - getNextPane: function() { - var tabs = _.toArray(this.tabs); - var panes = _.toArray(this._subviews); - - var i = 1 + _.indexOf(tabs, this.activePane.cid); - if (i > panes.length - 1) i = 0; - - return panes[i]; - }, - - getPane: function(name) { - var vid = this.tabs[name]; - return this._subviews[vid]; - }, - - getActivePane: function() { - return this.activePane; - }, - - size: function() { - return _.size(this.tabs); - }, - - clean: function() { - this.removeTabs(); - cdb.core.View.prototype.clean.call(this) - }, - - removeTab: function(name) { - if (this.tabs[name] !== undefined) { - var vid = this.tabs[name]; - this._subviews[vid].clean(); - delete this.tabs[name]; - - if (this.activeTab == name) { - this.activeTab = null; - } - - if (_.size(this.tabs)) { - this.active(_.keys(this.tabs)[0]); - } - } - }, - - removeTabs: function() { - for(var name in this.tabs) { - var vid = this.tabs[name]; - this._subviews[vid].clean(); - delete this.tabs[name]; - } - this.activeTab = null; - }, - - active: function(name) { - var - self = this, - vid = this.tabs[name]; - - if (vid !== undefined) { - - if (this.activeTab !== name) { - - var v = this._subviews[vid]; - - if (this.activeTab) { - var vid_old = this._subviews[this.tabs[this.activeTab]]; - - vid_old.hide(); - self.trigger('tabDisabled', this.activeTab , vid_old); - self.trigger('tabDisabled:' + this.activeTab, vid_old); - if(vid_old.deactivated) { - vid_old.deactivated(); - } - } - - v.show(); - if(v.activated) { - v.activated(); - } - - this.activeTab = name; - this.activePane = v; - - self.trigger('tabEnabled', name, v); - self.trigger('tabEnabled:' + name, v); - } - - return this.activePane; - } - }, - - render: function() { - return this; - }, - - each: function(fn) { - var self = this; - _.each(this.tabs, function(cid, tab) { - fn(tab, self.getPane(tab)); - }); - } - -}); -/** - * generic dialog - * - * this opens a dialog in the middle of the screen rendering - * a dialog using cdb.templates 'common/dialog' or template_base option. - * - * inherit class should implement render_content (it could return another widget) - * - * usage example: - * - * var MyDialog = cdb.ui.common.Dialog.extend({ - * render_content: function() { - * return "my content"; - * }, - * }) - * var dialog = new MyDialog({ - * title: 'test', - * description: 'long description here', - * template_base: $('#base_template').html(), - * width: 500 - * }); - * - * $('body').append(dialog.render().el); - * dialog.open(); - * - * TODO: implement draggable - * TODO: modal - * TODO: document modal_type - */ - -cdb.ui.common.Dialog = cdb.core.View.extend({ - - tagName: 'div', - className: 'dialog', - - events: { - 'click .ok': '_ok', - 'click .cancel': '_cancel', - 'click .close': '_cancel' - }, - - default_options: { - title: 'title', - description: '', - ok_title: 'Ok', - cancel_title: 'Cancel', - width: 300, - height: 200, - clean_on_hide: false, - enter_to_confirm: false, - template_name: 'common/views/dialog_base', - ok_button_classes: 'button green', - cancel_button_classes: '', - modal_type: '', - modal_class: '', - include_footer: true, - additionalButtons: [] - }, - - initialize: function() { - _.defaults(this.options, this.default_options); - - _.bindAll(this, 'render', '_keydown'); - - // Keydown bindings for the dialog - $(document).bind('keydown', this._keydown); - - // After removing the dialog, cleaning other bindings - this.bind("clean", this._reClean); - - this.template_base = this.options.template_base ? _.template(this.options.template_base) : cdb.templates.getTemplate(this.options.template_name); - }, - - render: function() { - var $el = this.$el; - - $el.html(this.template_base(this.options)); - - $el.find(".modal").css({ - width: this.options.width - //height: this.options.height - //'margin-left': -this.options.width>>1, - //'margin-top': -this.options.height>>1 - }); - - if(this.render_content) { - - this.$('.content').append(this.render_content()); - } - - if(this.options.modal_class) { - this.$el.addClass(this.options.modal_class); - } - - return this; - }, - - - _keydown: function(e) { - // If clicks esc, goodbye! - if (e.keyCode === 27) { - this._cancel(); - // If clicks enter, same as you click on ok button. - } else if (e.keyCode === 13 && this.options.enter_to_confirm) { - this._ok(); - } - }, - - /** - * helper method that renders the dialog and appends it to body - */ - appendToBody: function() { - $('body').append(this.render().el); - return this; - }, - - _ok: function(ev) { - - if(ev) ev.preventDefault(); - - if (this.ok) { - this.ok(this.result); - } - - this.hide(); - - }, - - _cancel: function(ev) { - - if (ev) { - ev.preventDefault(); - ev.stopPropagation(); - } - - if (this.cancel) { - this.cancel(); - } - - this.hide(); - - }, - - hide: function() { - - this.$el.hide(); - - if (this.options.clean_on_hide) { - this.clean(); - } - - }, - - open: function() { - - this.$el.show(); - - }, - - _reClean: function() { - - $(document).unbind('keydown', this._keydown); - - } - -}); -/** - * generic embbed notification, like twitter "new notifications" - * - * it shows slowly the notification with a message and a close button. - * Optionally you can set a timeout to close - * - * usage example: - * - var notification = new cdb.ui.common.Notificaiton({ - el: "#notification_element", - msg: "error!", - timeout: 1000 - }); - notification.show(); - // close it - notification.close(); -*/ - -cdb.ui.common.Notification = cdb.core.View.extend({ - - tagName: 'div', - className: 'dialog', - - events: { - 'click .close': 'hide' - }, - - default_options: { - timeout: 0, - msg: '', - hideMethod: '', - duration: 'normal' - }, - - initialize: function() { - this.closeTimeout = -1; - _.defaults(this.options, this.default_options); - this.template = this.options.template ? _.template(this.options.template) : cdb.templates.getTemplate('common/notification'); - - this.$el.hide(); - }, - - render: function() { - var $el = this.$el; - $el.html(this.template(this.options)); - if(this.render_content) { - this.$('.content').append(this.render_content()); - } - return this; - }, - - hide: function(ev) { - var self = this; - if (ev) - ev.preventDefault(); - clearTimeout(this.closeTimeout); - if(this.options.hideMethod != '' && this.$el.is(":visible") ) { - this.$el[this.options.hideMethod](this.options.duration, 'swing', function() { - self.$el.html(''); - self.trigger('notificationDeleted'); - self.remove(); - }); - } else { - this.$el.hide(); - self.$el.html(''); - self.trigger('notificationDeleted'); - self.remove(); - } - - }, - - open: function(method, options) { - this.render(); - this.$el.show(method, options); - if(this.options.timeout) { - this.closeTimeout = setTimeout(_.bind(this.hide, this), this.options.timeout); - } - } - -}); - -/** - * generic table - * - * this class creates a HTML table based on Table model (see below) and modify it based on model changes - * - * usage example: - * - var table = new Table({ - model: table - }); - - $('body').append(table.render().el); - - * model should be a collection of Rows - - */ - -/** - * represents a table row - */ -cdb.ui.common.Row = cdb.core.Model.extend({ -}); - -cdb.ui.common.TableData = Backbone.Collection.extend({ - model: cdb.ui.common.Row, - fetched: false, - - initialize: function() { - var self = this; - this.bind('reset', function() { - self.fetched = true; - }) - }, - - /** - * get value for row index and columnName - */ - getCell: function(index, columnName) { - var r = this.at(index); - if(!r) { - return null; - } - return r.get(columnName); - }, - - isEmpty: function() { - return this.length === 0; - } - -}); - -/** - * contains information about the table, mainly the schema - */ -cdb.ui.common.TableProperties = cdb.core.Model.extend({ - - columnNames: function() { - return _.map(this.get('schema'), function(c) { - return c[0]; - }); - }, - - columnName: function(idx) { - return this.columnNames()[idx]; - } -}); - -/** - * renders a table row - */ -cdb.ui.common.RowView = cdb.core.View.extend({ - tagName: 'tr', - - initialize: function() { - - this.model.bind('change', this.render, this); - this.model.bind('destroy', this.clean, this); - this.model.bind('remove', this.clean, this); - this.model.bind('change', this.triggerChange, this); - this.model.bind('sync', this.triggerSync, this); - this.model.bind('error', this.triggerError, this); - - this.add_related_model(this.model); - this.order = this.options.order; - }, - - triggerChange: function() { - this.trigger('changeRow'); - }, - - triggerSync: function() { - this.trigger('syncRow'); - }, - - triggerError: function() { - this.trigger('errorRow') - }, - - valueView: function(colName, value) { - return value; - }, - - render: function() { - var self = this; - var row = this.model; - - var tr = ''; - - var tdIndex = 0; - var td; - if(this.options.row_header) { - td = '
            '; - } else { - td = ''; - } - var v = self.valueView('', ''); - if(v.html) { - v = v[0].outerHTML; - } - td += v; - td += ''; - var v = self.valueView(key, value); - if(v.html) { - v = v[0].outerHTML; - } - td += v; - td += '
            ").append(self.headerView(['', 'header']))); - } else { - tr.append($("").append(self.headerView(['', 'header']))); - } - _(this.model.get('schema')).each(function(col) { - tr.append($("").append(self.headerView(col))); - }); - thead.append(tr); - return thead; - }, - - /** - * remove all rows - */ - clear_rows: function() { - this.$('tfoot').remove(); - this.$('tr.noRows').remove(); - - // unbind rows before cleaning them when all are gonna be removed - var rowView = null; - while(rowView = this.rowViews.pop()) { - // this is a hack to avoid all the elements are removed one by one - rowView.unbind(null, null, this); - // each element removes itself from rowViews - rowView.clean(); - } - // clean all the html at the same time - this.rowViews = []; - }, - - /** - * add rows - */ - addRow: function(row, collection, options) { - var self = this; - var tr = new self.rowView({ - model: row, - order: this.model.columnNames(), - row_header: this.options.row_header - }); - tr.tableView = this; - - tr.bind('clean', function() { - var idx = _.indexOf(self.rowViews, tr); - self.rowViews.splice(idx, 1); - // update index - for(var i = idx; i < self.rowViews.length; ++i) { - self.rowViews[i].$el.attr('data-y', i); - } - }, this); - tr.bind('changeRow', this.rowChanged, this); - tr.bind('saved', this.rowSynched, this); - tr.bind('errorSaving', this.rowFailed, this); - tr.bind('saving', this.rowSaving, this); - this.retrigger('saving', tr); - - tr.render(); - if(options && options.index !== undefined && options.index != self.rowViews.length) { - - tr.$el.insertBefore(self.rowViews[options.index].$el); - self.rowViews.splice(options.index, 0, tr); - //tr.$el.attr('data-y', options.index); - // change others view data-y attribute - for(var i = options.index; i < self.rowViews.length; ++i) { - self.rowViews[i].$el.attr('data-y', i); - } - } else { - // at the end - tr.$el.attr('data-y', self.rowViews.length); - self.$el.append(tr.el); - self.rowViews.push(tr); - } - - this.trigger('createRow'); - }, - - /** - * Callback executed when a row change - * @method rowChanged - * @abstract - */ - rowChanged: function() {}, - - /** - * Callback executed when a row is sync - * @method rowSynched - * @abstract - */ - rowSynched: function() {}, - - /** - * Callback executed when a row fails to reach the server - * @method rowFailed - * @abstract - */ - rowFailed: function() {}, - - /** - * Callback executed when a row send a POST to the server - * @abstract - */ - rowSaving: function() {}, - - /** - * Callback executed when a row is being destroyed - * @method rowDestroyed - * @abstract - */ - rowDestroying: function() {}, - - /** - * Callback executed when a row gets destroyed - * @method rowDestroyed - * @abstract - */ - rowDestroyed: function() {}, - - /** - * Callback executed when a row gets destroyed and the table data is empty - * @method emptyTable - * @abstract - */ - emptyTable: function() {}, - - /** - * Checks if the table is empty - * @method isEmptyTable - * @returns boolean - */ - isEmptyTable: function() { - return (this.dataModel.length === 0 && this.dataModel.fetched) - }, - - /** - * render only data rows - */ - _renderRows: function() { - this.clear_rows(); - if(! this.isEmptyTable()) { - if(this.dataModel.fetched) { - var self = this; - - this.dataModel.each(function(row) { - self.addRow(row); - }); - } else { - this._renderLoading(); - } - } else { - this._renderEmpty(); - } - - }, - - _renderLoading: function() { - }, - - _renderEmpty: function() { - }, - - /** - * Method for the children to redefine with the table behaviour when it has no rows. - * @method addEmptyTableInfo - * @abstract - */ - addEmptyTableInfo: function() { - // #to be overwrite by descendant classes - }, - - /** - * render table - */ - render: function() { - var self = this; - - // render header - self.$el.html(self._renderHeader()); - - // render data - self._renderRows(); - - return this; - - }, - - /** - * return jquery cell element of cell x,y - */ - getCell: function(x, y) { - if(this.options.row_header) { - ++y; - } - return this.rowViews[y].getCell(x); - }, - - _cellClick: function(e, evtName) { - evtName = evtName || 'cellClick'; - e.preventDefault(); - var cell = $(e.currentTarget || e.target); - var x = parseInt(cell.attr('data-x'), 10); - var y = parseInt(cell.parent().attr('data-y'), 10); - this.trigger(evtName, e, cell, x, y); - }, - - _cellDblClick: function(e) { - this._cellClick(e, 'cellDblClick'); - } - - -}); -(function() { - -/** - * this module implements all the features related to overlay geometries - * in leaflet: markers, polygons, lines and so on - */ - - -/** - * view for markers - */ -function PointView(geometryModel) { - var self = this; - // events to link - var events = [ - 'click', - 'dblclick', - 'mousedown', - 'mouseover', - 'mouseout', - 'dragstart', - 'drag', - 'dragend' - ]; - - this._eventHandlers = {}; - this.model = geometryModel; - this.points = []; - - this.geom = L.GeoJSON.geometryToLayer(geometryModel.get('geojson'), function(geojson, latLng) { - //TODO: create marker depending on the visualizacion options - var p = L.marker(latLng,{ - icon: L.icon({ - iconUrl: cdb.config.get('assets_url') + '/images/layout/default_marker.png', - iconAnchor: [11, 11] - }) - }); - - var i; - for(i = 0; i < events.length; ++i) { - var e = events[i]; - p.on(e, self._eventHandler(e)); - } - return p; - }); - - this.bind('dragend', function(e, pos) { - geometryModel.set({ - geojson: { - type: 'Point', - //geojson is lng,lat - coordinates: [pos[1], pos[0]] - } - }); - }); -} - -PointView.prototype = new GeometryView(); - -PointView.prototype.edit = function() { - this.geom.dragging.enable(); -}; - -/** - * returns a function to handle events fot evtType - */ -PointView.prototype._eventHandler = function(evtType) { - var self = this; - var h = this._eventHandlers[evtType]; - if(!h) { - h = function(e) { - var latlng = e.target.getLatLng(); - var s = [latlng.lat, latlng.lng]; - self.trigger(evtType, e.originalEvent, s); - }; - this._eventHandlers[evtType] = h; - } - return h; -}; - -/** - * view for other geometries (polygons/lines) - */ -function PathView(geometryModel) { - var self = this; - // events to link - var events = [ - 'click', - 'dblclick', - 'mousedown', - 'mouseover', - 'mouseout', - ]; - - this._eventHandlers = {}; - this.model = geometryModel; - this.points = []; - - - this.geom = L.GeoJSON.geometryToLayer(geometryModel.get('geojson')); - this.geom.setStyle(geometryModel.get('style')); - - - /*for(var i = 0; i < events.length; ++i) { - var e = events[i]; - this.geom.on(e, self._eventHandler(e)); - }*/ - -} - -PathView.prototype = new GeometryView(); - -PathView.prototype._leafletLayers = function() { - // check if this is a multi-feature or single-feature - if (this.geom.getLayers) { - return this.geom.getLayers(); - } - return [this.geom]; -}; - - -PathView.prototype.enableEdit = function() { - var self = this; - var layers = this._leafletLayers(); - _.each(layers, function(g) { - g.setStyle(self.model.get('style')); - g.on('edit', function() { - self.model.set('geojson', self.geom.toGeoJSON().geometry); - }, self); - }); -}; - -PathView.prototype.disableEdit = function() { - var self = this; - var layers = this._leafletLayers(); - _.each(layers, function(g) { - g.off('edit', null, self); - }); -}; - -PathView.prototype.edit = function(enable) { - var self = this; - var fn = enable ? 'enable': 'disable'; - var layers = this._leafletLayers(); - _.each(layers, function(g) { - g.editing[fn](); - enable ? self.enableEdit(): self.disableEdit(); - }); -}; - -cdb.geo.leaflet = cdb.geo.leaflet || {}; - -cdb.geo.leaflet.PointView = PointView; -cdb.geo.leaflet.PathView = PathView; - - -})(); -(function() { -/** - * view for markers - */ -function PointView(geometryModel) { - var self = this; - // events to link - var events = [ - 'click', - 'dblclick', - 'mousedown', - 'mouseover', - 'mouseout', - 'dragstart', - 'drag', - 'dragend' - ]; - - this._eventHandlers = {}; - this.model = geometryModel; - this.points = []; - - var style = _.clone(geometryModel.get('style')) || {}; - //style.path = google.maps.SymbolPath.CIRCLE; - //style.scale = style.weight; - //style.strokeColor = "ff0000"; - //style.strokeOpacity = 1; - //style.strokeWeight = 1; - //style.fillColor = '00000'; - //style.fillOpacity = 0.5; - - this.geom = new GeoJSON ( - geometryModel.get('geojson'), - { - icon: { - url: cdb.config.get('assets_url') + '/images/layout/default_marker.png', - anchor: {x: 10, y: 10} - }, - raiseOnDrag: false, - crossOnDrag: false - } - ); - - // bind events - var i; - for(i = 0; i < events.length; ++i) { - var e = events[i]; - google.maps.event.addListener(this.geom, e, self._eventHandler(e)); - } - - // link dragging - this.bind('dragend', function(e, pos) { - geometryModel.set({ - geojson: { - type: 'Point', - // geojson is lng,lat - coordinates: [pos[1], pos[0]] - } - }); - }); -} - -PointView.prototype = new GeometryView(); - -PointView.prototype._eventHandler = function(evtType) { - var self = this; - var h = this._eventHandlers[evtType]; - if(!h) { - h = function(e) { - var latlng = e.latLng; - var s = [latlng.lat(), latlng.lng()]; - self.trigger(evtType, e, s); - }; - this._eventHandlers[evtType] = h; - } - return h; -}; - -PointView.prototype.edit = function(enable) { - this.geom.setDraggable(enable); -}; - -/** - * view for other geometries (polygons/lines) - */ -function PathView(geometryModel) { - var self = this; - // events to link - var events = [ - 'click', - 'dblclick', - 'mousedown', - 'mouseover', - 'mouseout', - ]; - - this._eventHandlers = {}; - this.model = geometryModel; - this.points = []; - - - - var style = _.clone(geometryModel.get('style')) || {}; - - this.geom = new GeoJSON ( - geometryModel.get('geojson'), - style - ); - - /*_.each(this.geom._layers, function(g) { - g.setStyle(geometryModel.get('style')); - g.on('edit', function() { - geometryModel.set('geojson', L.GeoJSON.toGeoJSON(self.geom)); - }, self); - }); - */ - - _.bindAll(this, '_updateModel'); - var self = this; - - function bindPath(p) { - google.maps.event.addListener(p, 'insert_at', self._updateModel); - /* - google.maps.event.addListener(p, 'remove_at', this._updateModel); - google.maps.event.addListener(p, 'set_at', this._updateModel); - */ - } - - // TODO: check this conditions - - if(this.geom.getPaths) { - var paths = this.geom.getPaths(); - - if (paths && paths[0]) { - // More than one path - for(var i = 0; i < paths.length; ++i) { - bindPath(paths[i]); - } - } else { - // One path - bindPath(paths); - google.maps.event.addListener(this.geom, 'mouseup', this._updateModel); - } - } else { - // More than one path - if (this.geom.length) { - for(var i = 0; i < this.geom.length; ++i) { - bindPath(this.geom[i].getPath()); - google.maps.event.addListener(this.geom[i], 'mouseup', this._updateModel); - } - } else { - // One path - bindPath(this.geom.getPath()); - google.maps.event.addListener(this.geom, 'mouseup', this._updateModel); - } - } - - /*for(var i = 0; i < events.length; ++i) { - var e = events[i]; - this.geom.on(e, self._eventHandler(e)); - }*/ - -} - -PathView.prototype = new GeometryView(); - -PathView.getGeoJSON = function(geom, gType) { - - var coordFn = { - 'Polygon': 'getPath', - 'MultiPolygon': 'getPath', - 'LineString': 'getPath', - 'MultiLineString': 'getPath', - 'Point': 'getPosition', - 'MultiPoint': 'getPosition' - }; - - function _coord(latlng) { - return [latlng.lng(), latlng.lat()]; - } - - function _coords(latlngs) { - var c = []; - for(var i = 0; i < latlngs.length; ++i) { - c.push(_coord(latlngs.getAt(i))); - } - return c; - } - - // single - if(!geom.length || geom.length == 1) { - var g = geom.length ? geom[0]: geom; - var coords; - if(gType == 'Point') { - coords = _coord(g.getPosition()); - } else if(gType == 'MultiPoint') { - coords = [_coord(g.getPosition())] - } else if(gType == 'Polygon') { - coords = [_coords(g.getPath())]; - coords[0].push(_coord(g.getPath().getAt(0))); - } else if(gType == 'MultiPolygon') { - coords = []; - for(var p = 0; p < g.getPaths().length; ++p) { - var c = _coords(g.getPaths().getAt(p)); - c.push(_coord(g.getPaths().getAt(p).getAt(0))); - coords.push(c); - } - coords = [coords] - } else if(gType == 'LineString') { - coords = _coords(g.getPath()); - } else if(gType == 'MultiLineString') { - //TODO: redo - coords = [_coords(g.getPath())]; - } - return { - type: gType, - coordinates: coords - } - } else { - // poly - var c = []; - for(var i = 0; i < geom.length; ++i) { - c.push(PathView.getGeoJSON(geom[i], gType).coordinates[0]); - } - return { - type: gType, - coordinates: c - } - } -} - -PathView.prototype._updateModel = function(e) { - var self = this; - setTimeout(function() { - self.model.set('geojson', PathView.getGeoJSON(self.geom, self.model.get('geojson').type )); - }, 100) -} - -PathView.prototype.edit = function(enable) { - - var fn = enable ? 'enable': 'disable'; - var g = this.geom.length ? this.geom: [this.geom]; - for(var i = 0; i < g.length; ++i) { - g[i].setEditable(enable); - } - if(!enable) { - this.model.set('geojson', PathView.getGeoJSON(this.geom, this.model.get('geojson').type)); - } -}; - -cdb.geo.gmaps = cdb.geo.gmaps || {}; - -cdb.geo.gmaps.PointView = PointView; -cdb.geo.gmaps.PathView = PathView; - - - -})(); - - - cdb.$ = $; - cdb.L = L; - cdb.Mustache = Mustache; - cdb.Backbone = Backbone; - cdb._ = _; - - })(); - - - - - ; - for(var i in __prev) { - // keep it at global context if it didn't exist - if(__prev[i]) { - window[i] = __prev[i]; - } - } - - -})(); diff --git a/vendor/assets/stylesheets/cartodb.css b/vendor/assets/stylesheets/cartodb.css deleted file mode 100644 index 1fedf8c8d7..0000000000 --- a/vendor/assets/stylesheets/cartodb.css +++ /dev/null @@ -1,4230 +0,0 @@ - - /** - * CartoDB infowindow dark styles - */ - - div.cartodb-popup.dark .jspContainer:after { - background: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, rgba(0,0,0,0)), color-stop(100%, rgba(0,0,0,1))); - background: -webkit-linear-gradient(top, rgba(0,0,0,0), rgba(0,0,0,1)); - background: -moz-linear-gradient(top, rgba(0,0,0,0), rgba(0,0,0,1)); - background: -o-linear-gradient(top, rgba(0,0,0,0), rgba(0,0,0,1)); - background: linear-gradient(top, rgba(0,0,0,0), rgba(0,0,0,1)); - } - - div.cartodb-popup.dark .jspContainer:before { - background: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, rgba(0,0,0,1)), color-stop(100%, rgba(0,0,0,0))); - background: -webkit-linear-gradient(top, rgba(0,0,0,1), rgba(0,0,0,0)); - background: -moz-linear-gradient(top, rgba(0,0,0,1), rgba(0,0,0,0)); - background: -o-linear-gradient(top, rgba(0,0,0,1), rgba(0,0,0,0)); - background: linear-gradient(top, rgba(0,0,0,1), rgba(0,0,0,0)); - } - - div.cartodb-popup.dark { - background:url('../img/dark.png') no-repeat -226px 0; - } - - div.cartodb-popup.dark div.cartodb-popup-content-wrapper { - background:url('../img/dark.png') repeat-y -452px 0; - } - - div.cartodb-popup.dark div.cartodb-popup-tip-container { - background:url('../img/dark.png') no-repeat 0 0; - } - - div.cartodb-popup.dark a.cartodb-popup-close-button { - background:url('../img/dark.png') no-repeat 0 -23px; - } - - div.cartodb-popup.dark h4 { - color:#999; - } - - div.cartodb-popup.dark p { - color:#FFFFFF; - } - - div.cartodb-popup.dark a { - color:#397DB9; - } - - div.cartodb-popup.dark p.empty { - font-style:italic; - color:#AAA; - } - - div.cartodb-popup.dark .jspDrag { - background: #AAAAAA; - background: rgba(255,255,255,0.5); - } - - div.cartodb-popup.dark .jspDrag:hover { - background: #DEDEDE; - background: rgba(255,255,255,0.8); - } - - - - /* NEW CartoDB 2.0 dark popups */ - - div.cartodb-popup.v2.dark { - background:#000000; - } - - div.cartodb-popup.v2.dark:before { - border-top-color:black; - } - - div.cartodb-popup.v2.dark div.cartodb-popup-tip-container:after { - border-top-color:#000; - } - - div.cartodb-popup.v2.dark a.cartodb-popup-close-button { - background:#000000; - } - - div.cartodb-popup.v2.dark a.cartodb-popup-close-button:before, - div.cartodb-popup.v2.dark a.cartodb-popup-close-button:after { - background:white; - } - - /* Hello IE */ - @media \0screen\,screen\9 { - div.cartodb-popup.v2.dark { - border:4px solid #AAA; - } - - div.cartodb-popup.v2.dark div.cartodb-popup-tip-container { - border-top:18px solid #000; - } - - div.cartodb-popup.v2.dark a.cartodb-popup-close-button { - border:2px solid #AAA; - color:white; - } - - div.cartodb-popup.v2.dark a.cartodb-popup-close-button:hover { - border:2px solid #BBB; - } - } - /** - * CartoDB popup styles (DEFAULT) - */ - - div.cartodb-infowindow { - position: absolute; - z-index: 12; - } - - div.cartodb-popup { - position:relative; - width:226px; - height:auto; - padding:7px 0 0 0; - margin:0; - background:url('../img/light.png') no-repeat -226px 0; - } - - div.cartodb-popup div.cartodb-popup-content-wrapper { - width:190px; - max-width: 190px; - padding:12px 19px 12px 19px; - overflow-x: hidden; - background:url('../img/light.png') repeat-y -452px 0; - } - - div.cartodb-popup div.cartodb-popup-content { - display:block; - width:190px; - max-width: 190px; - min-height:5px; - height:auto; - max-height:185px; - margin: 0; - padding: 0; - overflow-y: auto; - overflow-x: hidden!important; - outline: none; - text-align:left; - } - - /* Custom gradients for scroll content */ - - div.cartodb-popup .jspContainer:after, - div.cartodb-popup .jspContainer:before { - content:''; - position:absolute; - left:0; - right:12px; - display:block; - height:10px; - width:190px; - z-index: 5; - } - - div.cartodb-popup .jspContainer:after { - bottom:0px; - background: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, rgba(255,255,255,0)), color-stop(100%, rgba(255,255,255,1))); - background: -webkit-linear-gradient(top, rgba(255,255,255,0), rgba(255,255,255,1)); - background: -moz-linear-gradient(top, rgba(255,255,255,0), rgba(255,255,255,1)); - background: -o-linear-gradient(top, rgba(255,255,255,0), rgba(255,255,255,1)); - background: linear-gradient(top, rgba(255,255,255,0), rgba(255,255,255,1)); - filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#ffffff', endColorstr='#00ffffff',GradientType=0 ); - } - - div.cartodb-popup .jspContainer:before { - top:0px; - background: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, rgba(255,255,255,1)), color-stop(100%, rgba(255,255,255,0))); - background: -webkit-linear-gradient(top, rgba(255,255,255,1), rgba(255,255,255,0)); - background: -moz-linear-gradient(top, rgba(255,255,255,1), rgba(255,255,255,0)); - background: -o-linear-gradient(top, rgba(255,255,255,1), rgba(255,255,255,0)); - background: linear-gradient(top, rgba(255,255,255,1), rgba(255,255,255,0)); - filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#ffffff', endColorstr='#00ffffff',GradientType=0 ); - } - - div.cartodb-popup div.cartodb-popup-tip-container { - width:226px; - height:20px; - background:url('../img/light.png') no-repeat 0 0; - } - - div.cartodb-popup a.cartodb-popup-close-button { - position:absolute; - top:-9px; - right:-9px; - width:26px; - height:26px; - padding:0; - background:url('../img/light.png') no-repeat 0 -23px; - text-indent:-9999px; - font-size:0; - line-height:0; - opacity:1; - -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=100)"; - filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=1); - filter: alpha(opacity=100); - text-transform:uppercase; - z-index:3; - } - - /* When there are no fields in header popup themes */ - div.cartodb-popup.header.no_fields div.cartodb-popup-content { - display:none; - } - div.cartodb-popup.header.no_fields - div.cartodb-popup-content-wrapper - div.cartodb-edit-buttons { - padding-top:5px; - margin-top:0; - } - div.cartodb-popup.header.no_fields div.cartodb-edit-buttons { - border: none; - padding-top:0; - } - - - /* Custom scroll in CartoDB content */ - - div.cartodb-popup .jspContainer { - overflow: hidden; - position: relative; - outline: none; - } - - div.cartodb-popup .jspContainer * { - outline: none; - } - - div.cartodb-popup .jspPane { - position: absolute; - padding:4px 0 0 0!important; - z-index:1; - } - - div.cartodb-popup .jspVerticalBar { - position: absolute; - top: 0; - right: 0; - width: 6px; - height: 100%; - background: none; - z-index:10; - } - - div.cartodb-popup .jspHorizontalBar { - position: absolute; - bottom: 0; left: 0; - width: 100%; - height: 6px; - background: none; - } - - div.cartodb-popup .jspVerticalBar *, - div.cartodb-popup .jspHorizontalBar * { - margin: 0; - padding: 0; - } - - div.cartodb-popup .jspCap { - display: none; - } - - div.cartodb-popup .jspHorizontalBar .jspCap { - float: left; - } - - div.cartodb-popup .jspTrack { - position: relative; - cursor: pointer; - background: none; - } - - div.cartodb-popup .jspDrag { - position: relative; - top: 0; left: 0; - cursor: pointer; - border-radius:10px; - -moz-border-radius:10px; - -webkit-border-radius:10px; - background: #999999; - background: rgba(0,0,0,0.16); - } - - div.cartodb-popup .jspDrag:hover { - background: #666666; - background: rgba(0,0,0,0.5); - cursor: pointer; - } - - div.cartodb-popup .jspHorizontalBar .jspTrack, - div.cartodb-popup .jspHorizontalBar .jspDrag { - float: left; - height: 100%; - } - - div.cartodb-popup .jspArrow { - background: #50506d; - text-indent: -20000px; - display: block; - cursor: pointer; - } - - div.cartodb-popup .jspArrow.jspDisabled { - cursor: default; - background: #80808d; - } - - div.cartodb-popup .jspVerticalBar .jspArrow { - height: 16px; - } - - div.cartodb-popup .jspHorizontalBar .jspArrow { - width: 16px; - float: left; - height: 100%; - } - - div.cartodb-popup .jspVerticalBar .jspArrow:focus { - outline: none; - } - - div.cartodb-popup .jspCorner { - background: #eeeef4; - float: left; - height: 100%; - } - - * html div.cartodb-popup .jspCorner { - margin: 0 -3px 0 0; - } - - - /* CartoDB light content styles */ - div.cartodb-popup h2 { - line-height:normal; - } - - div.cartodb-popup h4 { - display:block; - width:190px; - margin: 0; - padding: 0; - font:bold 11px "Helvetica Neue","Helvetica",Arial; - color:#CCCCCC; - text-transform:uppercase; - word-wrap: break-word; - } - - div.cartodb-popup p { - display:block; - width:190px; - max-width: 190px; - margin: 0; - padding:0 0 7px; - font:normal 13px "Helvetica",Arial; - color:#333333; - word-wrap: break-word; - } - - div.cartodb-popup p.italic { - font-style: italic; - } - - div.cartodb-popup p.loading { - position:relative; - display:block; - width:170px; - max-width: 170px; - margin: 0; - padding:0 0 0 30px; - font:normal 13px "Helvetica",Arial; - color:#888; - font-style:italic; - word-wrap: break-word; - line-height:21px; - } - - div.cartodb-popup p.error { - position:relative; - display:block; - width:170px; - max-width:170px; - margin:0; - padding:0; - font:normal 13px "Helvetica",Arial; - color:#FF7F7F; - font-style:italic; - word-wrap: break-word; - line-height:18px; - } - - div.cartodb-popup p.empty { - color:#999999; - font-style: italic; - } - - div.cartodb-popup div.spinner { - position:absolute!important; - display:inline; - top:0; - left:0; - margin:10px 0 0 10px; - } - - - /* NEW CartoDB 2.0 popups */ - - div.cartodb-popup.v2 { - width:226px; - padding:0; - margin:0 0 14px 0; - background:none; - -moz-box-shadow: 0 0 0 4px rgba(0,0,0,0.15); - -webkit-box-shadow: 0 0 0 4px rgba(0,0,0,0.15); - box-shadow: 0 0 0 4px rgba(0,0,0,0.15); - -webkit-border-radius:2px; - -moz-border-radius:2px; - border-radius:2px; - background:white; - } - - div.cartodb-popup.v2:before { - content:''; - position:absolute; - bottom:-14px; - left:0; - width:0; - height:0; - margin-left:28px; - border-left:0px solid transparent; - border-right:14px solid transparent; - border-top:14px solid white; - z-index:2; - } - - div.cartodb-popup.v2 - div.cartodb-popup-content-wrapper { - width: auto; - max-width: none; - padding:12px; - -webkit-border-radius:2px; - -moz-border-radius:2px; - border-radius:2px; - background:none; - } - - div.cartodb-popup.v2 - div.cartodb-popup-content { - width:auto; - max-width:none; - display:block; - background:none; - } - - div.cartodb-popup.v2 div.cartodb-popup-content p, - div.cartodb-popup.v2 div.cartodb-popup-content h4 { - width:auto; - max-width:95%; - display:block; - } - - div.cartodb-popup.v2 div.cartodb-popup-tip-container { - position:absolute; - bottom:-20px; - left:-4px; - width:20px; - height:16px; - margin-left:28px; - background:none; - overflow:hidden; - z-index:0; - } - - div.cartodb-popup.v2 div.cartodb-popup-tip-container:before { - content:''; - position:absolute; - width:20px; - height:20px; - left:0; - top:-10px; - margin-left:0; - -ms-transform: skew(0,-45deg); - -webkit-transform: skew(0,-45deg); - transform: skew(0,-45deg); - border-radius:0 0 0 10px; - background:rgba(0,0,0,0.15); - z-index:0; - } - - div.cartodb-popup.v2 a.cartodb-popup-close-button { - right:-12px; - top:-12px; - width:20px; - height:20px; - background:white; - -webkit-border-radius:18px; - -moz-border-radius:18px; - border-radius:18px; - box-shadow:0 0 0 3px rgba(0,0,0,0.15); - } - - div.cartodb-popup.v2 a.cartodb-popup-close-button:before, - div.cartodb-popup.v2 a.cartodb-popup-close-button:after { - content:''; - position:absolute; - top:9px; - left:6px; - width:8px; - height:2px; - background:#397DBA; - -webkit-border-radius:3px; - -moz-border-radius:3px; - border-radius:3px; - } - - div.cartodb-popup.v2 a.cartodb-popup-close-button:before { - -ms-transform: rotate(45deg); - -webkit-transform: rotate(45deg); - transform: rotate(45deg); - } - - div.cartodb-popup.v2 a.cartodb-popup-close-button:after { - -ms-transform: rotate(-45deg); - -webkit-transform: rotate(-45deg); - transform: rotate(-45deg); - } - - div.cartodb-popup.v2 a.cartodb-popup-close-button:hover { - box-shadow:0 0 0 3px rgba(0,0,0,0.25); - } - - /* Hello IE */ - @media \0screen\,screen\9 { - div.cartodb-popup.v2 { - border:4px solid #CCC; - } - - div.cartodb-popup.v2 div.cartodb-popup-tip-container { - position:absolute; - width:0; - height:0; - margin-left:28px; - z-index:2; - bottom:-18px; - left:-4px; - border-left:0px solid transparent; - border-right:18px solid transparent; - border-top:18px solid white; - } - - div.cartodb-popup.v2 a.cartodb-popup-close-button { - right:-14px; - top:-14px; - width:18px; - padding:0 0 0 2px; - text-indent:0; - font:bold 11px Arial; - font-weight:700; - text-decoration:none; - text-align:center; - line-height:20px; - border:2px solid #CCC; - } - - div.cartodb-popup.v2 a.cartodb-popup-close-button:before, - div.cartodb-popup.v2 a.cartodb-popup-close-button:after { - display:none; - } - - div.cartodb-popup.v2 a.cartodb-popup-close-button:hover { - border:2px solid #999; - } - } - -/** - * CartoDB blue header popup styles - */ - -div.cartodb-popup.header.blue div.cartodb-popup-header { - background:url('../img/headers.png') no-repeat 0 -40px; -} - -div.cartodb-popup.header.blue.header .cartodb-popup-header a { - color:white; -} - -div.cartodb-popup.header.blue div.cartodb-popup-header h4 { - color:#1F4C7F; -} - -div.cartodb-popup.header.blue div.cartodb-popup-header span.separator { - background:#225386; -} - -div.cartodb-popup.header.blue a.cartodb-popup-close-button { - background:url('../img/headers.png') no-repeat -226px -40px; -} - -div.cartodb-popup.header.blue a.cartodb-popup-close-button:hover { - background-position:-226px -66px; -} - - -/* NEW CartoDB 2.0 blue header popups */ - -div.cartodb-popup.v2.header.blue div.cartodb-popup-header { - background: none; - background: -ms-linear-gradient(top, #4F9CD7, #2B68A8); - background: -o-linear-gradient(right, #4F9CD7, #2B68A8); - background: -webkit-linear-gradient(top, #4F9CD7, #2B68A8); - background: -moz-linear-gradient(right, #4F9CD7, #2B68A8); - -ms-filter: "progid:DXImageTransform.Microsoft.Gradient(startColorStr='#4F9CD7',endColorStr='#2B68A8',GradientType=0)"; -} - -div.cartodb-popup.v2.header.blue a.cartodb-popup-close-button { - background:white; -} -/** - * CartoDB header popup styles (DEFAULT) - */ - -div.cartodb-popup.header { - padding:0; - background:none; - box-shadow:none; - -webkit-box-shadow:none; - -moz-box-shadow:none; - -o-box-shadow:none; - border-bottom:none; - border-radius:0; - -webkit-border-radius:0; - -moz-border-radius:0; - -o-border-radius:0; -} - -div.cartodb-popup.header div.cartodb-popup-header { - position:relative; - width:188px; - height:auto; - max-height:62px; - overflow:hidden; - padding:17px 19px 17px 19px; - background:url('../img/headers.png') no-repeat 0 -40px; -} - - -div.cartodb-popup.header div.cartodb-popup-header h1 { - width:100%; - margin:0; - font:bold 21px "Helvetica Neue", "Helvetica", Arial; - color:#FFFFFF; - line-height:23px; - text-shadow: 0 1px rgba(0,0,0,0.5); - word-wrap:break-word; -} - -div.cartodb-popup.header div.cartodb-popup-header h1 a { - color:white; - font-size:21px; - word-wrap:break-word; -} - -div.cartodb-popup.header div.cartodb-popup-header h1 a:hover { - text-decoration: underline; -} - -div.cartodb-popup.header div.cartodb-popup-header h1.loading { - position:relative; - display:block; - width:auto; - padding-right:0; - padding-left:30px; - font-size:14px; - font-weight:normal; - line-height:19px; -} - -div.cartodb-popup.header div.cartodb-popup-header h1.error { - position:relative; - display:block; - width:auto; - padding-right:0; - padding-left:0; - font-size:14px; - font-weight:normal; - font-style: italic; - line-height:19px; -} - -div.cartodb-popup.header div.cartodb-popup-header h4 { - color:#1F4C7F; -} - -div.cartodb-popup.header div.cartodb-popup-header span.separator { - position:absolute; - bottom:0; - left:4px; - right:4px; - height:1px; - background:#225386; -} - -div.cartodb-popup.header div.cartodb-popup-content { - max-height:150px; -} - -div.cartodb-popup.header a.cartodb-popup-close-button { - background:url('../img/headers.png') no-repeat -226px -40px; -} - -div.cartodb-popup.header a.cartodb-popup-close-button:hover { - background-position:-226px -66px; -} - - - -/* NEW CartoDB 2.0 header popups */ - -div.cartodb-popup.header.v2.header { - -moz-box-shadow: 0 0 0 4px rgba(0,0,0,0.15); - -webkit-box-shadow: 0 0 0 4px rgba(0,0,0,0.15); - box-shadow: 0 0 0 4px rgba(0,0,0,0.15); - -webkit-border-radius:2px; - -moz-border-radius:2px; - border-radius:2px; - background:white; -} - -div.cartodb-popup.v2.header div.cartodb-popup-header { - position:relative; - width:auto; - height:auto; - max-height:62px; - overflow:hidden; - padding:17px 12px; - background: none; - background: -ms-linear-gradient(top, #4F9CD7, #2B68A8); - background: -o-linear-gradient(right, #4F9CD7, #2B68A8); - background: -webkit-linear-gradient(top, #4F9CD7, #2B68A8); - background: -moz-linear-gradient(right, #4F9CD7, #2B68A8); - -ms-filter: "progid:DXImageTransform.Microsoft.Gradient(startColorStr='#4F9CD7',endColorStr='#2B68A8',GradientType=0)"; - - -webkit-border-top-left-radius: 2px; - -webkit-border-top-right-radius: 2px; - -moz-border-radius-topleft: 2px; - -moz-border-radius-topright: 2px; - border-top-left-radius: 2px; - border-top-right-radius: 2px; -} - -div.cartodb-popup.v2.header div.cartodb-popup-header:before { - content:''; - position:absolute; - bottom:0; - left:0; - right:0; - width:100%; - height:1px; - background:rgba(0,0,0,0.1); -} - -div.cartodb-popup.v2.header a.cartodb-popup-close-button { - right:-12px; - top:-12px; - width:20px; - height:20px; - background:white; - -webkit-border-radius:18px; - -moz-border-radius:18px; - border-radius:18px; - box-shadow:0 0 0 3px rgba(0,0,0,0.15); -} - -div.cartodb-popup.v2.header a.cartodb-popup-close-button:before, -div.cartodb-popup.v2.header a.cartodb-popup-close-button:after { - content:''; - position:absolute; - top:9px; - left:6px; - width:8px; - height:2px; - background:#397DBA; - -webkit-border-radius:3px; - -moz-border-radius:3px; - border-radius:3px; -} - -div.cartodb-popup.v2.header a.cartodb-popup-close-button:before { - -ms-transform: rotate(45deg); - -webkit-transform: rotate(45deg); - transform: rotate(45deg); -} - -div.cartodb-popup.v2.header a.cartodb-popup-close-button:after { - -ms-transform: rotate(-45deg); - -webkit-transform: rotate(-45deg); - transform: rotate(-45deg); -} - -div.cartodb-popup.v2.header a.cartodb-popup-close-button:hover { - box-shadow:0 0 0 3px rgba(0,0,0,0.25); -} - -/* Hello IE */ -@media \0screen\,screen\9 { - - div.cartodb-popup.header.v2 { - border-bottom:4px solid #CCC; - } - - div.cartodb-popup.v2.header div.cartodb-popup-header { - background:#3B7FBD; - -ms-filter: progid:DXImageTransform.Microsoft.Gradient(startColorStr='#4F9CD7',endColorStr='#2B68A8',GradientType=0); - } - -} - -/** - * CartoDB green header popup styles - */ - -div.cartodb-popup.header.green div.cartodb-popup-header { - background:url('../img/headers.png') no-repeat -252px -40px; -} - -div.cartodb-popup.header.green div.cartodb-popup-header h4 { - color:#00916D; -} - -div.cartodb-popup.header.green div.cartodb-popup-header span.separator { - background:#008E6A; -} - -div.cartodb-popup.header.green a.cartodb-popup-close-button { - background:url('../img/headers.png') no-repeat -478px -40px; -} - -div.cartodb-popup.header.green a.cartodb-popup-close-button:hover { - background-position:-478px -66px; -} - - -/* NEW CartoDB 2.0 green header popups */ - -div.cartodb-popup.v2.header.green div.cartodb-popup-header { - background: none; - background: -ms-linear-gradient(top, #00CC99, #00B185); - background: -o-linear-gradient(right, #00CC99, #00B185); - background: -webkit-linear-gradient(top, #00CC99, #00B185); - background: -moz-linear-gradient(right, #00CC99, #00B185); - -ms-filter: "progid:DXImageTransform.Microsoft.Gradient(startColorStr='#00CC99',endColorStr='#00B185',GradientType=0)"; -} - -div.cartodb-popup.v2.header.green a.cartodb-popup-close-button { - background:white; -} - -div.cartodb-popup.v2.header.green a.cartodb-popup-close-button:before, -div.cartodb-popup.v2.header.green a.cartodb-popup-close-button:after { - background:#00CC99; -} - -/* Hello IE */ -@media \0screen\,screen\9 { - div.cartodb-popup.v2.header.green a.cartodb-popup-close-button { - color:#00CC99; - } -} -/** - * CartoDB orange header popup styles - */ - -div.cartodb-popup.header.orange div.cartodb-popup-header { - background:url('../img/headers.png') no-repeat -756px -40px; -} - -div.cartodb-popup.header.orange div.cartodb-popup-header h4 { - color:#CC2929; -} - -div.cartodb-popup.header.orange div.cartodb-popup-header span.separator { - background:#CC2929; -} - -div.cartodb-popup.header.orange a.cartodb-popup-close-button { - background:url('../img/headers.png') no-repeat -982px -40px; -} - -div.cartodb-popup.header.orange a.cartodb-popup-close-button:hover { - background-position:-982px -66px; -} - - -/* NEW CartoDB 2.0 orange header popups */ - -div.cartodb-popup.v2.header.orange div.cartodb-popup-header { - background: none; - background: -ms-linear-gradient(top, #FF6825, #FF3333); - background: -o-linear-gradient(right, #FF6825, #FF3333); - background: -webkit-linear-gradient(top, #FF6825, #FF3333); - background: -moz-linear-gradient(right, #FF6825, #FF3333); - -ms-filter: "progid:DXImageTransform.Microsoft.Gradient(startColorStr='#FF6825',endColorStr='#FF3333',GradientType=0)"; -} - -div.cartodb-popup.v2.header.orange a.cartodb-popup-close-button { - background:white; -} - -div.cartodb-popup.v2.header.orange a.cartodb-popup-close-button:before, -div.cartodb-popup.v2.header.orange a.cartodb-popup-close-button:after { - background:#CC2929; -} - -/* Hello IE */ -@media \0screen\,screen\9 { - div.cartodb-popup.v2.header.orange a.cartodb-popup-close-button { - color:#CC2929; - } -} - /** - * CartoDB header with-image popup styles - */ - - div.cartodb-popup.header.with-image div.cartodb-popup-header { - position:relative; - - background:url('../img/headers.png') no-repeat -1008px 0; - height:138px; - max-height:104px; - } - - div.cartodb-popup.header.with-image div.cartodb-popup-header .cover { - display:block; - position:absolute; - overflow:hidden; - width: 218px; - height:135px; - top: 4px; - left: 4px; - border-radius: 4px 4px 0 0; - } - - div.cartodb-popup.header.with-image div.cartodb-popup-header .cover .shadow { - position:absolute; - width: 218px; - height:55px; - bottom: 0; - left: 0; - background:url('../img/shadow.png') no-repeat; - z-index: 100; - } - - div.cartodb-popup.header.with-image div.cartodb-popup-header .cover #spinner { - position:absolute; - top: 67px; - left: 109px; - } - - div.cartodb-popup.header.with-image div.cartodb-popup-header .cover img { - position:absolute; - border-radius: 4px 4px 0 0; - display:none; - } - - div.cartodb-popup.header.with-image div.cartodb-popup-header .image_not_found { - position:absolute; - top: 15px; - left: 15px; - width: 200px; - display:none; - } - - div.cartodb-popup.header.with-image div.cartodb-popup-header .image_not_found a { - display:-moz-inline-stack;display:inline-block;vertical-align:top;*vertical-align:auto;zoom:1;*display:inline; - margin: 3px 0 0 -2px; - color: #888888; - font-size:13px; - font-family: "Helvetica", "Helvetica Neue", Arial, sans-serif; - text-decoration: underline; - } - - div.cartodb-popup.header.with-image div.cartodb-popup-header .image_not_found a:hover { - color: #888888; - text-decoration:underline; - } - - div.cartodb-popup.header.with-image div.cartodb-popup-header .cover .image_not_found i { - display:-moz-inline-stack;display:inline-block;vertical-align:top;*vertical-align:auto;zoom:1;*display:inline; - width: 31px; - height: 22px; - background:transparent url('../img/image_not_found.png'); - } - - div.cartodb-popup.header.with-image div.cartodb-popup-header h1 { - position:absolute; - bottom: 13px; - left: 18px; - width: 188px; - z-index: 150; - } - - div.cartodb-popup.header.with-image div.cartodb-popup-header h4 { - color:#CCC; - } - - div.cartodb-popup.header.with-image div.cartodb-popup-header span.separator { - background:#CCC; - } - - div.cartodb-popup.header.with-image a.cartodb-popup-close-button { - background:url('../img/headers.png') no-repeat -226px -40px; - } - - div.cartodb-popup.header.with-image a.cartodb-popup-close-button:hover { - background-position:-226px -66px; - } - - div.cartodb-popup.header.with-image .cartodb-popup-header h1 { - display:none; - } - - div.cartodb-popup.header.with-image .cartodb-popup-header h1.order1 { - display:block; - } - - div.cartodb-popup.header.with-image .cartodb-popup-content-wrapper .order1 { - display:none; - } - - - /* NEW CartoDB 2.0 image header popups */ - - div.cartodb-popup.v2.header.with-image div.cartodb-popup-header { - background: #2C2C2C; - background: -ms-linear-gradient(top, #535353, #2C2C2C); - background: -o-linear-gradient(right, #535353, #2C2C2C); - background: -webkit-linear-gradient(top, #535353, #2C2C2C); - background: -moz-linear-gradient(right, #535353, #2C2C2C); - -ms-filter: "progid:DXImageTransform.Microsoft.Gradient(startColorStr='#535353',endColorStr='#2C2C2C',GradientType=0)"; - } - - div.cartodb-popup.v2.header.with-image div.cartodb-popup-header h1 { - width:85%; - } - - div.cartodb-popup.v2.header.with-image div.cartodb-popup-header span.separator { - left:0; - right:0; - background:#CCC; - } - - div.cartodb-popup.v2.header.with-image a.cartodb-popup-close-button { - background:white; - } - - div.cartodb-popup.v2.header.with-image div.cartodb-popup-header .cover { - display:block; - width:100%; - height:138px; - top:0; - left:0; - -moz-border-radius:2px 2px 0 0; - -webkit-border-radius:2px 2px 0 0; - border-radius:2px 2px 0 0; - overflow:hidden; - } - - div.cartodb-popup.v2.header.with-image div.cartodb-popup-header .cover .shadow { - width: 100%; - height:57px; - background: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, rgba(0,0,0,0)), color-stop(100%, rgba(0,0,0,0.8))); - background: -webkit-linear-gradient(top, rgba(0,0,0,0), rgba(0,0,0,0.8)); - background: -moz-linear-gradient(top, rgba(0,0,0,0), rgba(0,0,0,0.8)); - background: -o-linear-gradient(top, rgba(0,0,0,0), rgba(0,0,0,0.8)); - background: linear-gradient(top, rgba(0,0,0,0), rgba(0,0,0,0.8)); - filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#00000000', endColorstr='#000000',GradientType=0 ); - } - - div.cartodb-popup.v2.header.with-image div.cartodb-popup-header .cover img { - -moz-border-radius:2px 2px 0 0; - -webkit-border-radius:2px 2px 0 0; - border-radius:2px 2px 0 0; - } -/** - * CartoDB yellow header popup styles - */ - -div.cartodb-popup.header.yellow div.cartodb-popup-header { - background:url('../img/headers.png') no-repeat -504px -40px; -} - -div.cartodb-popup.header.yellow div.cartodb-popup-header h4 { - color:#D8832A; -} - -div.cartodb-popup.header.yellow div.cartodb-popup-header span.separator { - background:#CC7A29; -} - -div.cartodb-popup.header.yellow a.cartodb-popup-close-button { - background:url('../img/headers.png') no-repeat -730px -40px; -} - -div.cartodb-popup.header.yellow a.cartodb-popup-close-button:hover { - background-position:-730px -66px; -} - -/* NEW CartoDB 2.0 yellow header popups */ - -div.cartodb-popup.v2.header.yellow div.cartodb-popup-header { - background: none; - background: -ms-linear-gradient(top, #FFBF0D, #FF9933); - background: -o-linear-gradient(right, #FFBF0D, #FF9933); - background: -webkit-linear-gradient(top, #FFBF0D, #FF9933); - background: -moz-linear-gradient(right, #FFBF0D, #FF9933); - -ms-filter: "progid:DXImageTransform.Microsoft.Gradient(startColorStr='#FFBF0D',endColorStr='#FF9933',GradientType=0)"; -} - -div.cartodb-popup.v2.header.yellow a.cartodb-popup-close-button { - background:white; -} - -div.cartodb-popup.v2.header.yellow a.cartodb-popup-close-button:before, -div.cartodb-popup.v2.header.yellow a.cartodb-popup-close-button:after { - background:#CC7A29; -} - -/* Hello IE */ -@media \0screen\,screen\9 { - div.cartodb-popup.v2.header.yellow a.cartodb-popup-close-button { - color:#CC7A29; - } -} - /** - * CartoDB infowindow light styles - */ - - div.cartodb-popup h4 { - color:#CCCCCC; - } - - div.cartodb-popup p { - color:#333333; - } - - div.cartodb-popup p.loading { - color:#888; - } - - div.cartodb-popup p.error { - color:#FF7F7F; - } - - div.cartodb-popup p.empty { - color:#999999; - }/** - * CartoDB map style components - */ - -* { - -webkit-margin-after:0; - -webkit-margin-before:0; - -webkit-padding-start:0; - -webkit-padding-end:0; -} - -div.cartodb-share { - display:none; - position:relative; - float:right; - margin: 20px 20px 0 0; - z-index: 105; -} -div.cartodb-share a { - width: 14px; - height: 14px; - display: block; - color: #397DB8; - font-size:10px; - font-weight:bold; - text-transform: uppercase; - text-shadow: none; - padding: 7px 7px; - - background: #ffffff url('../img/share.png') no-repeat 7px 8px; - - -webkit-box-shadow: rgba(0, 0, 0, 0.2) 0 0 4px 2px; - -moz-box-shadow: rgba(0, 0, 0, 0.2) 0 0 4px 2px; - box-shadow: rgba(0, 0, 0, 0.2) 0 0 4px 2px; - -webkit-border-radius: 4px; - -moz-border-radius: 4px; - -ms-border-radius: 4px; - -o-border-radius: 4px; - border-radius: 4px; - border: 1px solid #999999; - - border-color: #C3C3C3; - - -webkit-border-radius: 4px; - -moz-border-radius: 4px; - -ms-border-radius: 4px; - -o-border-radius: 4px; - border-radius: 4px; - border: 1px solid #999999; -} -div.cartodb-share a:hover { - background: #ffffff url('../img/share.png') no-repeat -28px 8px; -} -div.cartodb-share a:active, div.cartodb-share a:hover:active { - background: #ffffff url('../img/share.png') no-repeat 7px 8px; -} - -.cartodb-fullscreen { - display:none; - position:relative; - margin: 11px 0 0 20px; - float:left; - clear:both; - - z-index: 105; -} -.cartodb-fullscreen a { - display:block; - width: 14px; - height: 14px; - padding: 7px; - - background: #ffffff url('../img/fullscreen.png') no-repeat 7px 3px; - - -webkit-box-shadow: rgba(0, 0, 0, 0.2) 0 0 4px 2px; - -moz-box-shadow: rgba(0, 0, 0, 0.2) 0 0 4px 2px; - box-shadow: rgba(0, 0, 0, 0.2) 0 0 4px 2px; - -webkit-border-radius: 4px; - -moz-border-radius: 4px; - -ms-border-radius: 4px; - -o-border-radius: 4px; - border-radius: 4px; - border: 1px solid #999999; -} -.cartodb-fullscreen a:active { - background-position: 7px 3px!important; -} -.cartodb-fullscreen a:hover { - background-position: -19px 5px; -} - -/* CartoDB Share Dialog styles */ - -.cartodb-share-dialog { - display:none; -} -.cartodb-share-dialog .mamufas { - position:fixed; - top:0; - left:0; - right:0; - bottom:0; - background:rgba(0,0,0, 0.5); - cursor: default; - z-index:1000001; -} -.cartodb-share-dialog .modal { - - position:absolute; - top: 50%; - left: 50%; - margin-left: -216px; - margin-top: -107px; - - webkit-box-shadow: rgba(0, 0, 0, 0.15) 0 0 0 4px; - -moz-box-shadow: rgba(0, 0, 0, 0.15) 0 0 0 4px; - box-shadow: rgba(0, 0, 0, 0.15) 0 0 0 4px; - - -webkit-border-radius: 4px; - -moz-border-radius: 4px; - -ms-border-radius: 4px; - -o-border-radius: 4px; - border-radius: 4px; - - border: 1px solid #999999; - - font-weight: bold; - font-family: "Segoe UI Bold", "Helvetica Bold", "Helvetica", Arial; - color: #333; - line-height: normal; -} -.cartodb-share-dialog.small .modal { - margin-left: -108px; - margin-top: -165px; -} -.cartodb-share-dialog.small .block .buttons { - margin: 0 0 10px 0; -} -.cartodb-share-dialog.small .block .buttons ul { - border:none; - padding: 0; -} - -.cartodb-share-dialog.small .block .content .embed_code { - padding: 0; -} -.cartodb-share-dialog .modal a.close { - position:absolute; - top:-15px; - right:-15px; - width:30px; - height:15px; - padding:7px 0 8px; - background:white; - font:normal 13px "Helvetica",Arial; - text-decoration:none; - - webkit-box-shadow: rgba(0, 0, 0, 0.15) 0 0 0 4px; - -moz-box-shadow: rgba(0, 0, 0, 0.15) 0 0 0 4px; - box-shadow: rgba(0, 0, 0, 0.15) 0 0 0 4px; - - -webkit-border-radius: 50px; - -moz-border-radius: 50px; - -ms-border-radius: 50px; - -o-border-radius: 50px; - border-radius: 50px; - - line-height:14px; - text-align:center; - z-index:105; -} - -.cartodb-share-dialog .block { - background:white; - -webkit-border-radius: 4px; - -moz-border-radius: 4px; - -ms-border-radius: 4px; - -o-border-radius: 4px; - border-radius: 4px; - - webkit-box-shadow: rgba(0, 0, 0, 0.15) 0 0 4px 3px; - -moz-box-shadow: rgba(0, 0, 0, 0.15) 0 0 4px 3px; - box-shadow: rgba(0, 0, 0, 0.15) 0 0 4px 3px; - -} - -.cartodb-share-dialog .block .buttons ul { - margin: 0; - padding: 0 24px 0 0; - border-right: 1px solid #E5E5E5; -} - -.cartodb-share-dialog .block .buttons li { - list-style:none; - margin: 0 0 4px 0; - padding: 0; -} - -.cartodb-share-dialog .block .buttons li a { - display:block; - padding: 10px 13px 11px 30px; - width: 121px; - font-size: 13px; - font-weight:bold; - color:#fff; - background:#3D8FCA; - - text-decoration:none; - - -webkit-border-radius: 3px; - -moz-border-radius: 3px; - -ms-border-radius: 3px; - -o-border-radius: 3px; - border-radius: 3px; -} - - -/* iPhone landscape */ -@media only screen -and (min-device-width : 320px) -and (max-device-width : 480px) -and (orientation : landscape) { - - /*.cartodb-map-wrapper div.cartodb-overlay.overlay-text.desktop { display:none; }*/ - /*.cartodb-map-wrapper div.cartodb-overlay.overlay-text.mobile { display:block; }*/ - -} - -@media only screen and (min-width: 360px) and (max-width: 490px) { - - /*div.cartodb-overlay.overlay-text.desktop { display:none; }*/ - /*div.cartodb-overlay.overlay-text.mobile { display:block; }*/ - -} - -/* iPhone portrait */ -@media only screen -and (min-device-width : 320px) -and (max-device-width : 480px) { - - /*.cartodb-map-wrapper div.cartodb-overlay.overlay-text.desktop { display:none; }*/ - /*.cartodb-map-wrapper div.cartodb-overlay.overlay-text.mobile { display:block; }*/ - - div.cartodb-header h1 { - width:78%; - } - div.cartodb-header > p { - width:80%; - } - -} - -/* iPad */ -@media only screen -and (min-device-width : 768px) -and (max-device-width : 1024px) { - - div.cartodb-header h1 { - width:78%; - } - div.cartodb-header > p { - width:80%; - } - -} - -@media -only screen and (-webkit-min-device-pixel-ratio: 2), -only screen and ( min--moz-device-pixel-ratio: 2), -only screen and ( -o-min-device-pixel-ratio: 2/1), -only screen and ( min-device-pixel-ratio: 2), -only screen and ( min-resolution: 192dpi), -only screen and ( min-resolution: 2dppx) { - - div.cartodb-header h1 { - width:78%; - } - div.cartodb-header > p { - width:80%; - } - - div.cartodb-zoom a { - background:url('../img/other@2x.png') no-repeat 0 0!Important; - background-size: 113px 34px!Important; - } - - div.cartodb-zoom a.zoom_in { - background-position: -68px 9px!important - } - - div.cartodb-zoom a.zoom_out { - background-position:-94px 10px!important; - } - - div.cartodb-header div.social a.facebook { - background:url('../img/other@2x.png') no-repeat 0 0!Important; - background-size: 113px 34px!Important; - } - - div.cartodb-header div.social a.twitter { - background:url('../img/other@2x.png') no-repeat -26px 0!Important; - background-size: 113px 34px!Important; - } - - div.cartodb-searchbox span.loader { - background: url('../img/loader@2x.gif') no-repeat center center white!Important; - background-size: 16px 16px!Important; - } - div.cartodb-mobile .aside div.cartodb-searchbox span.loader { - background: url('../img/dark_loader@2x.gif') no-repeat center center #292929!Important; - background-size: 16px 16px!Important; - } - - - div.cartodb-tiles-loader div.loader { - background: url('../img/loader@2x.gif') no-repeat center center white!Important; - background-size: 16px 16px!Important; - } - - div.cartodb-searchbox input.submit { - background:url('../img/other@2x.png') no-repeat -56px 0!Important; - background-size: 113px 34px!Important; - } - - .cartodb-mobile .aside .cartodb-searchbox input.submit { - background:url('../img/mobile_zoom.png') no-repeat 0 0!Important; - } - - -} - -.cartodb-share-dialog .block .buttons li a.twitter { - background:#3D8FCA url('../img/twitter.png') no-repeat 10px 50%; -} -.cartodb-share-dialog .block .buttons li a.twitter:hover { background-color:#3272A0; } - -.cartodb-share-dialog .block .buttons li a.facebook { - background:#3B5998 url('../img/facebook.png') no-repeat 10px 50%; -} -.cartodb-share-dialog .block .buttons li a.facebook:hover { background-color: #283C65; } - -.cartodb-share-dialog .block .buttons li a.link { - background:#f37f7b url('../img/link.png') no-repeat 10px 50%; -} -.cartodb-share-dialog .block .buttons li a.link:hover { background-color:#DC6161; } - -.cartodb-share-dialog .block h3, .cartodb-share-dialog .block p, .cartodb-share-dialog .block a, .cartodb-share-dialog .block label {letter-spacing:0;} - -.cartodb-share-dialog .block div.head { - position:relative; - padding: 5px 26px; - border-bottom:1px solid #E5E5E5; -} - -.cartodb-share-dialog .block h3 { margin: 1em 0; font-size: 15px; font-weight: bold; } - -.cartodb-share-dialog .block h4 { - font-size: 13px; - font-weight: bold; - color: #666666; - padding: 0; margin: 0; - margin: 0 0 9px 0; -} - -.cartodb-share-dialog .block .content .buttons, -.cartodb-share-dialog .block .content .embed_code { - display:inline-block; zoom: 1; *display: inline; vertical-align:top; -} -.cartodb-share-dialog .block .content .embed_code { - padding-left: 24px; -} -.cartodb-share-dialog .block .content .embed_code textarea { - resize: none; - padding: 5px; - width: 153px; - height: 104px; - border: 1px solid #C3C3C3; - background: #F5F5F5; - font-size: 11px; - color: #666666; - -webkit-border-radius: 4px; - -moz-border-radius: 4px; - -ms-border-radius: 4px; - -o-border-radius: 4px; - border-radius: 4px; - -} - -.cartodb-share-dialog .block .content { - padding: 20px 26px 30px 26px; -} - -/* MOBILE */ - -.cartodb-mobile { - width: 100%; - height: 100%; - z-index: 100000000; -} -.cartodb-mobile .cartodb-header { - background: none; - z-index: 100000; -} -.cartodb-mobile .cartodb-header .content { - padding: 0; -} -.cartodb-mobile .cartodb-header .hgroup { - position:relative; - height: 40px; - padding: 10px; -} -.cartodb-mobile.with-fullscreen .cartodb-header .hgroup { - position: relative; - margin-left: 60px; - margin-right: 70px; -} - -.cartodb-mobile.with-header .cartodb-header .content .hgroup .title, -.cartodb-mobile.with-header .cartodb-header .content .hgroup .description { - display: block; -} -.cartodb-mobile .cartodb-header .content .title , -.cartodb-mobile .cartodb-header .content .description { - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; -} -.cartodb-mobile .cartodb-header .content .button { - height: 58px; - width: 58px; - background-color: rgba(0, 0, 0, 0.5); - line-height: normal; - z-index: 99999; -} -.cartodb-mobile.with-header .cartodb-header { - background-color: rgba(0, 0, 0, 0.5); -} -.cartodb-mobile.with-fullscreen .cartodb-header .content .fullscreen { - display:block; -} -.cartodb-mobile.with-header .cartodb-header .content .fullscreen { - background: none; -} -.cartodb-mobile .cartodb-header .content .fullscreen { - display: none; - position:relative; - top: 0px; - left: 0px; - float: left; - width: 60px; - height: 60px; - margin: auto; - padding: 0; - background: rgba(0,0,0,.5); - cursor: pointer; - z-index: 10; - -webkit-border-radius: 0 0 5px 0; - -moz-border-radius: 0 0 5px 0; - -ms-border-radius: 0 0 5px 0; - -o-border-radius: 0 0 5px 0; - border-radius: 0 0 5px 0; - -webkit-transform-style: "ease-in"; - -moz-transform-style: "ease-in"; - -ms-transform-style: "ease-in"; - -o-transform-style: "ease-in"; - transform-style: "ease-in"; - -webkit-transition-property: background; - -moz-transition-property: background; - -o-transition-property: background; - transition-property: background; - -webkit-transition-duration: 150ms; - -moz-transition-duration: 150ms; - -o-transition-duration: 150ms; - transition-duration: 150ms; -} -.cartodb-mobile.with-header .cartodb-header .content .fullscreen { - border-right: 1px solid rgba(255, 255, 255, .35); - -webkit-border-radius: 0; - -moz-border-radius: 0; - -ms-border-radius: 0; - -o-border-radius: 0; - border-radius: 0; -} -.cartodb-mobile .cartodb-header .content .fullscreen:hover, -.cartodb-mobile.with-header .cartodb-header .content .fullscreen:hover { - background: rgba(0,0,0,.3); -} -.cartodb-mobile .cartodb-header .content .fullscreen:before { - content: ''; - width: 60px; - height: 60px; - background:url('../img/fullscreen_mobile.png') no-repeat 50% 50%; - background-size: 28px 28px; - position: absolute; -} -.cartodb-mobile.with-search .cartodb-header .content .toggle, -.cartodb-mobile.with-layers .cartodb-header .content .toggle { - display: block; -} -.cartodb-mobile .cartodb-header .content .toggle { - display: none; - position:relative; - top: 0; - right: 0; - float: right; - width: 70px; - height: 60px; - margin: auto; - padding: 0; - background: rgba(0,0,0, .5); - cursor: pointer; - z-index: 10; - -webkit-border-radius: 0 0 0 5px; - -moz-border-radius: 0 0 0 5px; - -ms-border-radius: 0 0 0 5px; - -o-border-radius: 0 0 0 5px; - border-radius: 0 0 0 5px; - -webkit-transform-style: "ease-in"; - -moz-transform-style: "ease-in"; - -ms-transform-style: "ease-in"; - -o-transform-style: "ease-in"; - transform-style: "ease-in"; - -webkit-transition-property: background; - -moz-transition-property: background; - -o-transition-property: background; - transition-property: background; - -webkit-transition-duration: 150ms; - -moz-transition-duration: 150ms; - -o-transition-duration: 150ms; - transition-duration: 150ms; -} -.cartodb-mobile .cartodb-header .content .toggle:hover, -.cartodb-mobile.with-header .cartodb-header .content .toggle:hover { - background: rgba(0,0,0,.3); -} -.cartodb-mobile.with-header .cartodb-header .content .toggle { - background: none; - border-left: 1px solid rgba(255, 255, 255, .35); - -webkit-border-radius: 0; - -moz-border-radius: 0; - -ms-border-radius: 0; - -o-border-radius: 0; - border-radius: 0; -} -.cartodb-mobile .cartodb-header .content .toggle:before { - content: ''; - width: 70px; - height: 60px; - background:url('../img/toggle_aside.png') no-repeat 50% 50%; - background-size: 30px 30px; - position: absolute; -} -.cartodb-mobile.with-zoom .cartodb-zoom { - position: absolute; - top: 0px; - z-index:100000; -} -.cartodb-mobile.with-fullscreen .cartodb-zoom, -.cartodb-mobile.with-zoom.with-header .cartodb-zoom { - top: 60px; -} -.cartodb-mobile .aside { - position:absolute; - width: 250px; - height: 100%; - top: 0; - right: -250px; - background:#2D2D2D; - cursor: default; - z-index: 1000010; -} -.cartodb-mobile .aside .cartodb-searchbox { - position: relative; - display: none; - float: none; - margin: 0; - width: 100%; - height: auto; - -webkit-box-shadow: none; - -moz-box-shadow: none; - box-shadow: none; - background: transparent; - -webkit-border-radius: 0; - -moz-border-radius: 0; - -ms-border-radius: 0; - -o-border-radius: 0; - border-radius: 0; - border:none; - border-bottom: 1px solid #505050; - z-index: 105; -} -.cartodb-mobile .aside .cartodb-searchbox input.text { - border: none; - position: initial; - top:initial; - left:initial; - height: 39px; - padding: 10px 18px; - width: 185px; - font-size: 13px; - color: #fff; -} -.cartodb-mobile .aside .cartodb-searchbox input.text::-webkit-input-placeholder { - font-style: italic; -} -.cartodb-mobile .aside .cartodb-searchbox input.text:-moz-placeholder { - /* Firefox 18- */ - font-style: italic; -} -.cartodb-mobile .aside .cartodb-searchbox input.text::-moz-placeholder { - /* Firefox 19+ */ - font-style: italic; -} -.cartodb-mobile .aside .cartodb-searchbox input.text:-ms-input-placeholder { - font-style: italic; -} -.cartodb-mobile .aside .cartodb-searchbox span.loader { - left: initial; - top: 18px; - right: 14px; - background: url('../img/dark_loader.gif') no-repeat center center; -} -.cartodb-mobile .aside .cartodb-searchbox input.submit { - right: 18px; - top: 23px; - background:#f1f1f1; - width: 14px; - height: 14px; - left:initial; - outline:none; - cursor:pointer; - background:url('../img/mobile_zoom.png') no-repeat 0 0; -} -.cartodb-mobile .aside .layer-container { - position:relative; - height: 100%; -} -.cartodb-mobile .aside .scrollpane { - width: 100%; - height: 100%; - overflow: hidden; - outline:none; - /*padding-bottom: 25px;*/ -} -.cartodb-mobile .aside .scrollpane .jspContainer { overflow: hidden; position: relative; } -.cartodb-mobile .aside .scrollpane .jspPane { position: absolute; } -.cartodb-mobile .aside .scrollpane .jspVerticalBar { position: absolute; top: 0; right: 7px; width: 5px; height: 100%; background: none; z-index: 20; } -.cartodb-mobile .aside .scrollpane .jspVerticalBar * { margin: 0; padding: 0; } -.cartodb-mobile .aside .scrollpane .jspCap { display: none; } -.cartodb-mobile .aside .scrollpane .jspTrack { background: none; position: relative; } -.cartodb-mobile .aside .scrollpane .jspDrag { background: rgba(#BBB, .5); border-radius:5px; position: relative; top: 0; left: 0; cursor: pointer; } -.cartodb-mobile .aside .scrollpane .jspArrow { background: none; text-indent: -20000px; display: block; cursor: pointer; } -.cartodb-mobile .aside .scrollpane .jspVerticalBar .jspArrow { height: 10px; } -.cartodb-mobile .aside .scrollpane .jspVerticalBar .jspArrow:focus { outline: none; } -.cartodb-mobile .aside .scrollpane .jspCorner { background: #eeeef4; float: left; height: 100%; } -.cartodb-mobile .aside .layer-container > h3 { - padding: 23px 20px; - color: #999999; - font: bold 12px "Helvetica", Arial, sans-serif; - text-transform: uppercase; - background: #292929; - border-bottom: 1px solid #585858; -} -.cartodb-mobile .aside .layer-container .layers { - margin: 0; - padding: 0 10px; -} -.cartodb-mobile .aside .layer-container .layers > li { - padding: 5px 10px; - color: #fff; - list-style: none; - border-bottom: 1px solid #585858; -} -.cartodb-mobile .aside .layer-container .layers > li:last-child h3, -.cartodb-mobile .aside .layer-container .layers > li:last-child { - border: none; -} -.cartodb-mobile .aside .layer-container .layers > li a.toggle { - background:none; - width: 21px; - height: 10px; - background: #191919; - position:relative; - top: 2px; - float: right; - -webkit-border-radius: 4px; - -moz-border-radius: 4px; - -ms-border-radius: 4px; - -o-border-radius: 4px; - border-radius: 4px; -} -.cartodb-mobile .aside .layer-container .layers > li a.toggle.hide { - display:none; -} -.cartodb-mobile .aside .layer-container .layers > li.hidden a.toggle:before { - left: 0; -} -.cartodb-mobile .aside .layer-container .layers > li a.toggle:before { - position:absolute; - content:''; - top:1px; - right:0; - width: 7px; - height: 7px; - - -webkit-border-radius: 100px; - -moz-border-radius: 100px; - -ms-border-radius: 100px; - -o-border-radius: 100px; - border-radius: 100px; - - background: #fff; - -} -.cartodb-mobile .aside .layer-container .layers > li h3 { - - font: bold 12px "Helvetica", Arial, sans-serif; - text-transform: uppercase; - padding: 12px 0 13px 0; -} - -.cartodb-mobile .aside .layer-container .layers > li.has-toggle h3 { - cursor: pointer; -} -.cartodb-mobile .aside .layer-container .layers > li.has-legend.hidden h3, -.cartodb-mobile .aside .layer-container .layers > li.hidden h3 { - color: #666666; - border:none; - padding: 12px 0 13px 0; -} -.cartodb-mobile .aside .layer-container .layers > li.hidden.has-legend div.cartodb-legend { - display:none!important; -} -.cartodb-mobile .aside .layer-container .layers > li.hidden.has-legend h3 { - margin-bottom: 0; -} -.cartodb-mobile .aside .layer-container .layers > li.has-legend h3 { - border-bottom: 1px solid #585858; -} -.cartodb-mobile .aside .layer-container .layers > li div.cartodb-legend { - position:relative; - border:none; - webkit-box-shadow: none; - -moz-box-shadow: none; - box-shadow: none; - background: none; - padding: 0; - margin: 10px 0 18px 0; - padding: 2px 0 0 0; - bottom: auto; - right: auto; - cursor: text; -} -.cartodb-mobile .aside .layer-container .layers > li div.cartodb-legend.bubble ul li.graph { - border:none; -} -.cartodb-mobile .aside .layer-container .layers > li div.cartodb-legend.bubble ul li.graph .bubbles { - background:url('../img/dark_bubbles.png') no-repeat 0 0; -} -.cartodb-mobile .aside .layer-container .layers > li div.cartodb-legend .graph { - border: 1px solid #1A1108; -} -.cartodb-mobile .aside .layer-container .layers > li div.cartodb-legend ul li { - height: auto; - padding: 0; - font-size: 12px; - color: #fff; - font-weight: normal; - font-family: "Helvetica", Arial, sans-serif; - text-transform: none; - line-height: normal; -} -.cartodb-mobile .aside .layer-container .layers > li div.cartodb-legend.intensity ul li.graph { - height: 22px; -} -.cartodb-mobile .aside .layer-container .layers > li div.cartodb-legend ul li .bullet { - margin-top: 2px; -} -.cartodb-mobile .aside .layer-container .layers > li div.cartodb-legend ul li.max, -.cartodb-mobile .aside .layer-container .layers > li div.cartodb-legend ul li.min { - font-size: 10px; -} -.cartodb-mobile div.cartodb-timeslider .slider-wrapper { - position:absolute; - top: 17px; -} -.cartodb-mobile div.cartodb-timeslider .slider { - width: 100%; -} -.cartodb-mobile div.cartodb-timeslider { - height:40px; - width:auto; - margin-bottom:0; - - -webkit-box-shadow: none; - -moz-box-shadow: none; - box-shadow: none; - - -webkit-border-radius: 0; - -moz-border-radius: 0; - -ms-border-radius: 0; - -o-border-radius: 0; - border-radius: 0; - - border: 1px solid #E5E5E5; - border-left: none; - border-right: none; - border-top: 1px solid rgba(0,0,0, .2); - z-index: 1000001; -} -.cartodb-mobile div.cartodb-timeslider .slider-wrapper { - display:block; - width:100%; - height:4px; - padding:0; -} -.cartodb-mobile div.cartodb-timeslider { - width:100%!important; -} -.cartodb-mobile div.cartodb-timeslider ul { - width:100%; - position:relative; - clear:both; - overflow:hidden; -} -.cartodb-mobile div.cartodb-timeslider ul li { - display: block; - background:#fff; - float:left; -} -.cartodb-mobile div.cartodb-timeslider ul li.controls { - width: 50px; -} -.cartodb-mobile div.cartodb-timeslider ul li.time { - width: 80px; -} -.cartodb-mobile div.cartodb-timeslider ul li.last { - position:absolute; - left:145px; - right: 10px; -} -.cartodb-mobile div.cartodb-timeslider ul li.controls a.button { - -webkit-border-radius: 0; - -moz-border-radius: 0; - -ms-border-radius: 0; - -o-border-radius: 0; - border-radius: 0; -} -.cartodb-mobile .cartodb-attribution { - display:none; - list-style:none; - background: #fff; - position: absolute; - padding: 9px 12px; - margin: 0; - right: 20px; - bottom: 20px; - color:#999999; - -webkit-border-radius: 3px; - -moz-border-radius: 3px; - -ms-border-radius: 3px; - -o-border-radius: 3px; - border-radius: 3px; - z-index: 10000001; - font:12px/1.5 "Helvetica Neue", Arial, Helvetica, sans-serif; -} -.cartodb-mobile .cartodb-attribution a { - color: #0078A8; -} -.cartodb-mobile .cartodb-attribution li { - padding: 0; - margin: 3px; - display:inline-block; zoom: 1; *display: inline; vertical-align:top; - color: #999999; -} -.cartodb-mobile .cartodb-attribution li a { - text-transform: capitalize; - color: #0078A8; -} -.cartodb-mobile .backdrop { - display:none; - position: absolute; - top: 0; left: 0; right:0; bottom: 0; - background: #000; - filter: alpha(opacity=20); - filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=20); - opacity: 0.2; - z-index: 10000000; -} -.cartodb-mobile.with-torque .cartodb-attribution-button { - bottom: 59px; -} -.cartodb-mobile .cartodb-attribution-button { - display: none; - width: 20px; - height: 20px; - background: #fff; - position: absolute; - right: 20px; - bottom: 20px; - color: #999999; - text-align: center; - text-decoration: none; - -webkit-border-radius: 100px; - -moz-border-radius: 100px; - -ms-border-radius: 100px; - -o-border-radius: 100px; - border-radius: 100px; - background: #fff url('../img/bg-attribution-button.png') no-repeat 49% 50%; - font:12px/1.5 "Helvetica Neue", Arial, Helvetica, sans-serif; - z-index: 10; -} - -.cartodb-mobile .cartodb-attribution-button:before { - position:absolute; - content: ''; - top: -3px; - left: -3px; - width: 20px; - height: 20px; - border: 3px solid rgba(0, 0, 0, 0.3); - -webkit-border-radius: 100px; - -moz-border-radius: 100px; - -ms-border-radius: 100px; - -o-border-radius: 100px; - border-radius: 100px; - - -webkit-transform-style: "ease-in"; - -moz-transform-style: "ease-in"; - -ms-transform-style: "ease-in"; - -o-transform-style: "ease-in"; - transform-style: "ease-in"; - -webkit-transition-property: border; - -moz-transition-property: border; - -o-transition-property: border; - transition-property: border; - -webkit-transition-duration: 150ms; - -moz-transition-duration: 150ms; - -o-transition-duration: 150ms; - transition-duration: 150ms; -} -.cartodb-mobile .cartodb-attribution-button:hover:before { - border: 3px solid rgba(0, 0, 0, 0.7); -} - -/* LEGENDS */ - -div.cartodb-legend-stack { - position:absolute; - bottom: 35px; - right: 20px; - display:none; - - webkit-box-shadow: rgba(0, 0, 0, 0.2) 0 0 4px 2px; - -moz-box-shadow: rgba(0, 0, 0, 0.2) 0 0 4px 2px; - box-shadow: rgba(0, 0, 0, 0.2) 0 0 4px 2px; - - -webkit-border-radius: 4px; - -moz-border-radius: 4px; - -ms-border-radius: 4px; - -o-border-radius: 4px; - border-radius: 4px; - - border: 1px solid #999999; - background: white; - z-index: 105; - - cursor: text; -} -div.cartodb-legend-stack div.cartodb-legend { - position:relative; - top: auto; right: auto; left: auto; bottom: auto; - background: none; - border:none; - margin: 0; - - -webkit-border-radius: 0; - -moz-border-radius: 0; - -ms-border-radius: 0; - -o-border-radius: 0; - border-radius: 0px; - - border-bottom: 1px solid #999; - - webkit-box-shadow: none; - -moz-box-shadow: none; - box-shadow: none; - - cursor: text; -} -div.cartodb-legend-stack div.cartodb-legend:last-child { - border-bottom: none; -} -div.cartodb-legend { - position:absolute; - bottom: 35px; - right: 20px; - padding: 13px 15px 14px 15px; - - font:normal 13px "Helvetica",Arial; - color:#858585; - text-align: left; - webkit-box-shadow: rgba(0, 0, 0, 0.2) 0 0 4px 2px; - -moz-box-shadow: rgba(0, 0, 0, 0.2) 0 0 4px 2px; - box-shadow: rgba(0, 0, 0, 0.2) 0 0 4px 2px; - -webkit-border-radius: 4px; - -moz-border-radius: 4px; - -ms-border-radius: 4px; - -o-border-radius: 4px; - border-radius: 4px; - - border: 1px solid #999999; - background: white; - z-index: 105; -} -div.cartodb-legend .legend-title { - margin: 0 0 10px 0; - text-align:left; - color:#666; - font-weight:bold; - font-size:11px; - text-transform: uppercase; -} -div.cartodb-legend ul { - padding: 0; - margin: 0; - list-style: none; -} -div.cartodb-legend ul li { - padding: 0; - margin: 0; - font-size: 10px; - color: #666666; - font-weight:bold; - font-family: "Helvetica", Arial; - text-transform: uppercase; - line-height: normal; -} -/* None legend */ -div.cartodb-legend-stack div.cartodb-legend.none, -div.cartodb-legend.none { - display:none; -} - -div.map div.cartodb-legend-stack div.cartodb-legend.wrapper .cartodb-legend { - padding: 0; - display:block; -} - -div.cartodb-legend.wrapper .cartodb-legend { - display:block; - padding: 0; -} - -/* Custom legend */ - -div.cartodb-legend.custom ul li, -div.cartodb-legend.category ul li, -div.cartodb-legend.color ul li { - position:relative; - margin: 0 0 7px 0; - font-size: 10px; - color: #666666; - font-weight:bold; - font-family: "Helvetica", Arial; - text-transform: uppercase; - text-align: left; - height: 10px; - line-height: 10px; - vertical-align:middle; -} - - -div.cartodb-legend.custom ul li.bkg, -div.cartodb-legend.category ul li.bkg, -div.cartodb-legend.color ul li.bkg { - height: 20px; - line-height: 24px; - margin: 0 0 15px 0; -} - -div.cartodb-legend.custom ul li.bkg .bullet, -div.cartodb-legend.category ul li.bkg .bullet, -div.cartodb-legend.color ul li.bkg .bullet { - height: 20px; - width: 20px; - border: 1px solid rgba(0, 0, 0, .3); - border:none; - background-size: 26px 26px!important; - background-position: center center!important; - -webkit-border-radius: 0; - -moz-border-radius: 0; - -ms-border-radius: 0; - -o-border-radius: 0; - border-radius: 0; -} - -div.cartodb-legend.custom ul li.bkg:last-child, -div.cartodb-legend.color ul li.bkg:last-child, -div.cartodb-legend.category ul li.bkg:last-child { margin: 0 0 5px 0; } - - -div.cartodb-legend.custom ul li:last-child, -div.cartodb-legend.color ul li:last-child, -div.cartodb-legend.category ul li:last-child { margin: 0; } - -div.cartodb-legend.custom ul li .bullet, -div.cartodb-legend.category ul li .bullet, -div.cartodb-legend.color ul li .bullet { - float:left; - margin: 0 5px 0 0; - width: 3px; - height: 3px; - - -webkit-border-radius: 50%; - -moz-border-radius: 50%; - -ms-border-radius: 50%; - -o-border-radius: 50%; - border-radius: 50%; - - padding: 2px; - background:#fff; - border: 1px solid rgba(0, 0, 0, .2); - z-index: 1000; -} - -/* Bubble legend */ -div.cartodb-legend.bubble { - text-align:center; -} - -div.cartodb-legend.bubble ul { - clear:both; - overflow: hidden; - - display: -moz-inline-stack; - display: inline-block; -} - -div.cartodb-legend.bubble ul li { - position:relative; - float: left; - top: 15px; -} - -div.cartodb-legend.bubble ul li.graph { - top: 0; - width: 120px; - height: 40px; - margin: 0 10px; - background: #f1f1f1; -} - -div.cartodb-legend.bubble ul li.graph .bubbles { - background:url('../img/bubbles.png') no-repeat 0 0; - width:120px; height:40px; -} - - -/* Choropleth legend */ -div.cartodb-legend.choropleth { - padding: 13px 15px 15px 15px; -} - -div.cartodb-legend.choropleth ul { - min-width: 210px; -} - -div.cartodb-legend.choropleth li.min { - float: left; - margin: 0 0 5px 0; -} - -div.cartodb-legend.choropleth li.max { - float: right; - margin: 0 0 5px 0; -} - -div.cartodb-legend.choropleth li.graph div { - width: 10px; - height: 22px; -} - -div.cartodb-legend.choropleth li.graph .quartile { display: table-cell; } -div.cartodb-legend.choropleth li.graph.count_7 .quartile { width: 30px; } -div.cartodb-legend.choropleth li.graph.count_5 .quartile { width: 42px; } -div.cartodb-legend.choropleth li.graph.count_3 .quartile { width: 70px; } - -div.cartodb-legend.choropleth li.graph .colors { - display: table-row; -} - -div.cartodb-legend.choropleth li.graph { - clear:both; - overflow:hidden; - - display: table; - - width: 100%; - height: 22px; - - -webkit-border-radius: 3px; - -moz-border-radius: 3px; - -ms-border-radius: 3px; - -o-border-radius: 3px; - border-radius: 3px; - - -moz-background-clip: padding; - -webkit-background-clip: padding; - background-clip: padding; - - /*box-shadow: inset 0px 0px 0px 1px rgba(0, 0, 0, 0.2);*/ - border: 1px solid #b3b3b3; -} - -/* Density legend */ -div.cartodb-legend.density { - padding: 13px 15px 15px 15px; -} - -div.cartodb-legend.density ul { - min-width: 210px; -} - -div.cartodb-legend.density li.min { - float: left; - margin: 0 0 5px 0; -} - -div.cartodb-legend.density li.max { - float: right; - margin: 0 0 5px 0; -} - -div.cartodb-legend.density li.graph div { - width: 10px; - height: 22px; -} - -div.cartodb-legend.density li.graph .quartile { display: table-cell; } -div.cartodb-legend.density li.graph.count_7 .quartile { width: 30px; } -div.cartodb-legend.density li.graph.count_5 .quartile { width: 42px; } -div.cartodb-legend.density li.graph.count_3 .quartile { width: 70px; } - -div.cartodb-legend.density li.graph .colors { - display: table-row; -} - -div.cartodb-legend.density li.graph { - clear:both; - overflow:hidden; - - display: table; - width: 100%; - height: 22px; - - -webkit-border-radius: 3px; - -moz-border-radius: 3px; - -ms-border-radius: 3px; - -o-border-radius: 3px; - border-radius: 3px; - - -moz-background-clip: padding; - -webkit-background-clip: padding; - background-clip: padding; - - /*box-shadow: inset 0px 0px 0px 1px rgba(0, 0, 0, 0.2);*/ - border: 1px solid #b3b3b3; -} - -/* Intensity legend */ - -div.cartodb-legend.intensity { - padding: 13px 15px 15px 15px; -} - -div.cartodb-legend.intensity ul { - min-width: 210px; -} - -div.cartodb-legend.intensity li.min { - float: left; - margin: 0 0 5px 0; -} - -div.cartodb-legend.intensity li.max { - float: right; - margin: 0 0 5px 0; -} - -div.cartodb-legend.intensity li.graph { - clear:both; - - width: 100%; - height: 22px; - background:#f1f1f1; - - -webkit-border-radius: 3px; - -moz-border-radius: 3px; - -ms-border-radius: 3px; - -o-border-radius: 3px; - border-radius: 3px; - - -moz-background-clip: padding; - -webkit-background-clip: padding; - background-clip: padding; - - /*border: 1px solid #b3b3b3;*/ - -webkit-box-shadow: inset 0px 0px 0px 1px rgba(0, 0, 0, 0.2); - -o-box-shadow: inset 0px 0px 0px 1px rgba(0, 0, 0, 0.2); - -moz-box-shadow: inset 0px 0px 0px 1px rgba(0, 0, 0, 0.2); - -ms-box-shadow: inset 0px 0px 0px 1px rgba(0, 0, 0, 0.2); - box-shadow: inset 0px 0px 0px 1px rgba(0, 0, 0, 0.2); - -} - -/* CartoDB Zoom styles */ - -div.cartodb-zoom { - position: relative; - float:left; - display:block; - margin: 20px 0 0 20px; - width: 28px; - -webkit-box-shadow: rgba(0, 0, 0, 0.2) 0 0 4px 2px; - -moz-box-shadow: rgba(0, 0, 0, 0.2) 0 0 4px 2px; - box-shadow: rgba(0, 0, 0, 0.2) 0 0 4px 2px; - background: white; - -webkit-border-radius: 4px; - -moz-border-radius: 4px; - -ms-border-radius: 4px; - -o-border-radius: 4px; - border-radius: 4px; - border: 1px solid #999999; - z-index: 105; -} - -div.cartodb-zoom a { - position:relative; - display: block; - width: 28px; - height: 28px; - padding: 0; - font: bold 20px "Arial"; - color: #999999; - text-align: center; - text-decoration: none; - text-indent: -9999px; - line-height: 0; - font-size: 0; - background:url('../img/other.png') no-repeat 0 0; -} - -div.cartodb-zoom a.zoom_in { - border-bottom: 1px solid #E6E6E6; - background-position:-68px 10px; - -webkit-border-top-left-radius: 4px; - -webkit-border-top-right-radius: 4px; - -moz-border-radius-topleft: 4px; - -moz-border-radius-topright: 4px; - border-top-left-radius: 4px; - border-top-right-radius: 4px; -} - -div.cartodb-zoom a.zoom_in:hover { - background-position:-68px -14px; - cursor: pointer; -} - -div.cartodb-zoom a.zoom_out { - background-position:-94px 10px; - -webkit-border-bottom-left-radius: 4px; - -webkit-border-bottom-right-radius: 4px; - -moz-border-radius-bottomleft: 4px; - -moz-border-radius-bottomright: 4px; - border-bottom-left-radius: 4px; - border-bottom-right-radius: 4px; -} - -div.cartodb-zoom a.zoom_out:hover { - background-position:-94px -14px; - cursor: pointer; -} - -div.cartodb-zoom a.disabled { - filter: alpha(opacity=20); - filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=20); - opacity: 0.2; -} - -div.cartodb-zoom a.disabled:hover { - cursor: default; - color: #999999; -} - - -/* CartoDB zoom info control */ - -div.cartodb-zoom-info { - position:absolute; - display:block; - top:100px; - left:20px; - margin:20px 0 0 0; - width: 28px; - height:28px; - font:normal 13px "Helvetica",Arial; - color:#858585; - text-align: center; - line-height: 28px; - -webkit-box-shadow: rgba(0, 0, 0, 0.2) 0 0 4px 2px; - -moz-box-shadow: rgba(0, 0, 0, 0.2) 0 0 4px 2px; - box-shadow: rgba(0, 0, 0, 0.2) 0 0 4px 2px; - -webkit-border-radius: 4px; - -moz-border-radius: 4px; - -ms-border-radius: 4px; - -o-border-radius: 4px; - border-radius: 4px; - border: 1px solid #999999; - background: white; - z-index: 105; -} - - -/* Tiles loader control */ - -div.cartodb-tiles-loader { - position:relative; - float:left; - display:block; - left:0; - top:0; -} - -div.cartodb-tiles-loader div.loader { - position:absolute; - display:block; - top:70px; - left:-30px; - margin:20px 0 0 0; - width: 28px; - height:28px; - -webkit-box-shadow: rgba(0, 0, 0, 0.2) 0 0 4px 2px; - -moz-box-shadow: rgba(0, 0, 0, 0.2) 0 0 4px 2px; - box-shadow: rgba(0, 0, 0, 0.2) 0 0 4px 2px; - background: url('../img/loader.gif') no-repeat center center white; - -webkit-border-radius: 4px; - -moz-border-radius: 4px; - -ms-border-radius: 4px; - -o-border-radius: 4px; - border-radius: 4px; - border: 1px solid #999999; - z-index: 105; -} - -/* CartoDB layer selector box */ - -div.cartodb-layer-selector-box { - display:none; - position: relative; - float:right; - margin: 20px 20px 0 0; - width: 142px; - height: 29px; - color: #CCCCCC; - font-size:13px; - -webkit-box-shadow: rgba(0, 0, 0, 0.2) 0 0 4px 2px; - -moz-box-shadow: rgba(0, 0, 0, 0.2) 0 0 4px 2px; - box-shadow: rgba(0, 0, 0, 0.2) 0 0 4px 2px; - background: white; - -webkit-border-radius: 4px; - -moz-border-radius: 4px; - -ms-border-radius: 4px; - -o-border-radius: 4px; - border-radius: 4px; - border: 1px solid #999999; - z-index:100000; -} - -div.cartodb-layer-selector-box a.layers { - float:left; - width: 126px; - padding: 6px 8px; - line-height:20px; - color: #CCC; - text-decoration:none; - font-family: "robotoregular", Helvetica, Arial, Sans-serif; -} - -div.cartodb-layer-selector-box a.layers:hover { - color:#bbb; -} -div.cartodb-layer-selector-box a.layers:hover .count { - background:#ccc; -} - -div.cartodb-layer-selector-box a.layers .count { - position:absolute; - right:6px; - top:6px; - width:auto; - padding: 3px 6px; - margin:0; - font-size:10px; - color: #fff; - line-height:12px; - background:#DDDDDD; - -webkit-border-radius: 2px; - -moz-border-radius: 2px; - -ms-border-radius: 2px; - -o-border-radius: 2px; - border-radius: 2px; -} -div.cartodb-layer-selector-box div.cartodb-dropdown { - padding:0; - margin:0; -} -div.cartodb-layer-selector-box div.cartodb-dropdown ul { - padding:0; - margin:0; - list-style:none; - border:1px solid 999999; -} -div.cartodb-layer-selector-box div.cartodb-dropdown ul li { - border-bottom:1px solid #EDEDED; - position:relative; -} -div.cartodb-layer-selector-box div.cartodb-dropdown ul li:last-child { - border-bottom:none; -} -div.cartodb-layer-selector-box div.cartodb-dropdown ul li:hover { - background:#fff; -} -div.cartodb-layer-selector-box div.cartodb-dropdown ul li a.layer { - display: -moz-inline-stack; - display: inline-block; - vertical-align: middle; - width:104px; - padding: 13px 13px 15px 13px; - zoom: 1; - color: #666666; - font:normal 13px "Helvetica Neue","Helvetica",Arial; - text-decoration:none; - overflow:hidden; - white-space:nowrap; - text-overflow: ellipsis; - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - -o-user-select: none; - user-select: none; -} -div.cartodb-layer-selector-box div.cartodb-dropdown ul li:hover a.layer { - text-decoration: underline; - color:#545454; -} -div.cartodb-layer-selector-box div.cartodb-dropdown ul li a.switch { - position:absolute; - - top: 13px; - right: 10px; - - text-indent:-9999px; - vertical-align:middle; - width:23px; - height:12px; - padding: 0; - -webkit-border-radius: 12px; - -moz-border-radius: 12px; - -ms-border-radius: 12px; - -o-border-radius: 12px; - border-radius: 12px; - - -webkit-transform-style: "linear"; - -moz-transform-style: "linear"; - -ms-transform-style: "linear"; - -o-transform-style: "linear"; - transform-style: "linear"; - -webkit-transition-property: left; - -moz-transition-property: left; - -o-transition-property: left; - transition-property: left; - -webkit-transition-duration: 180ms; - -moz-transition-duration: 180ms; - -o-transition-duration: 180ms; - transition-duration: 180ms; - - text-decoration:none; - border:1px solid #44759E; -} -div.cartodb-layer-selector-box div.cartodb-dropdown ul li a.switch:before { - position:absolute; - content:' '; - top:0; - left:0; - width:100%; - height:100%; - -webkit-border-radius: 12px; - -moz-border-radius: 12px; - -ms-border-radius: 12px; - -o-border-radius: 12px; - border-radius: 12px; - - background: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, rgba(0, 0, 0, 0.18)), color-stop(100%, rgba(0, 0, 0, 0))); - background: -webkit-linear-gradient(rgba(0, 0, 0, 0.18), rgba(0, 0, 0, 0)); - background: -moz-linear-gradient(rgba(0, 0, 0, 0.18), rgba(0, 0, 0, 0)); - background: -o-linear-gradient(rgba(0, 0, 0, 0.18), rgba(0, 0, 0, 0)); - background: linear-gradient(rgba(0, 0, 0, 0.18), rgba(0, 0, 0, 0)); - - z-index:0; -} -div.cartodb-layer-selector-box div.cartodb-dropdown ul li a.switch span.handle { - position: absolute; - top: 0px; - left: 12px; - width: 10px; - height: 10px; - -webkit-border-radius: 12px; - -moz-border-radius: 12px; - -ms-border-radius: 12px; - -o-border-radius: 12px; - border-radius: 12px; - border: 1px solid #44759e; - background: #F2F2F2; - z-index: 2; - -webkit-transform-style: "linear"; - -moz-transform-style: "linear"; - -ms-transform-style: "linear"; - -o-transform-style: "linear"; - transform-style: "linear"; - -webkit-transition-property: left; - -moz-transition-property: left; - -o-transition-property: left; - transition-property: left; - -webkit-transition-duration: 180ms; - -moz-transition-duration: 180ms; - -o-transition-duration: 180ms; - transition-duration: 180ms; -} -div.cartodb-layer-selector-box div.cartodb-dropdown ul li a.switch.enabled { - border-color:#44759E; - background:#56AFEF; -} -div.cartodb-layer-selector-box div.cartodb-dropdown ul li a.switch.enabled span.handle { - left:12px; - border-color:#44759E; -} -div.cartodb-layer-selector-box div.cartodb-dropdown ul li a.switch.disabled { - opacity:1; - -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=100)"; - filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=1); - filter: alpha(opacity=100); - border-color:#CCCCCC; - background:#D8D8D8; -} -div.cartodb-layer-selector-box div.cartodb-dropdown ul li a.switch span.handle { - left:0; - border-color:#999999; -} -div.cartodb-layer-selector-box div.cartodb-dropdown ul li a.switch:hover { - cursor:pointer!important; -} -div.cartodb-layer-selector-box div.cartodb-dropdown ul li a.switch.working { - opacity:0.5; - -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=50)"; - filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=.5); - filter: alpha(opacity=50); -} - -div.cartodb-layer-selector-box div.cartodb-dropdown ul li a.switch.working:hover {cursor:default!important;} - - -/* CartoDB search box control */ - -div.cartodb-searchbox { - position: relative; - display:none; - float:right; - margin: 20px 20px 0 0; - width: 142px; - height:29px; - -webkit-box-shadow: rgba(0, 0, 0, 0.2) 0 0 4px 2px; - -moz-box-shadow: rgba(0, 0, 0, 0.2) 0 0 4px 2px; - box-shadow: rgba(0, 0, 0, 0.2) 0 0 4px 2px; - background: white; - -webkit-border-radius: 4px; - -moz-border-radius: 4px; - -ms-border-radius: 4px; - -o-border-radius: 4px; - border-radius: 4px; - border: 1px solid #999999; - z-index:105; -} - -div.cartodb-searchbox span.loader { - position: absolute; - display:none; - top:3px; - left:3px; - width:22px; - height:22px; - background: url('../img/loader.gif') no-repeat center center white; - z-index:105; -} - -div.cartodb-searchbox input.text { - position: absolute; - top:6px; - left:30px; - width:103px; - padding:0; - margin:0; - line-height:17px; - border:none; - background:none; - border-bottom:1px dotted #CCCCCC; - -webkit-border-radius: 0; - -moz-border-radius: 0; - -ms-border-radius: 0; - -o-border-radius: 0; - border-radius: 0; - font:normal 14px Arial; - color:#999999; - text-align:left; - z-index:2; -} - -div.cartodb-searchbox input.text:focus { - outline:none; - border-color:#999999; - color:#666666; -} - -div.cartodb-searchbox input.submit { - position: absolute; - left:8px; - top:8px; - width:12px; - height:12px; - text-indent: -9999px; - font-size: 0; - line-height: 0; - text-transform: uppercase; - border:none; - background: url('../img/other.png') no-repeat -56px 0; - z-index:1; -} - -div.cartodb-searchbox input.submit:hover { - cursor:pointer; -} - - -/* CartoDB infobox control */ - -div.cartodb-infobox { - padding: 20px; - position: absolute; - display: inline-block; - -webkit-box-shadow: rgba(0, 0, 0, 0.2) 0 0 4px 2px; - -moz-box-shadow: rgba(0, 0, 0, 0.2) 0 0 4px 2px; - box-shadow: rgba(0, 0, 0, 0.2) 0 0 4px 2px; - background: white; - -webkit-border-radius: 4px; - -moz-border-radius: 4px; - -ms-border-radius: 4px; - -o-border-radius: 4px; - border-radius: 4px; - border: 1px solid #999999; - text-align:left; - z-index:105; -} - -/* CartoDB dropdown */ -div.cartodb-dropdown { - position:absolute; - display:none; - background:white; - -webkit-border-radius: 3px; - -moz-border-radius: 3px; - -ms-border-radius: 3px; - -o-border-radius: 3px; - border-radius: 3px; - border:none; - -webkit-box-shadow: rgba(0,0,0,0.2) 0 0 4px 1px; - -moz-box-shadow: rgba(0,0,0,0.2) 0 0 4px 1px; - -ms-box-shadow: rgba(0,0,0,0.2) 0 0 4px 1px; - -o-box-shadow: rgba(0,0,0,0.2) 0 0 4px 1px; - box-shadow: rgba(0,0,0,0.2) 0 0 4px 1px; - z-index:150; -} - -div.cartodb-dropdown.border { - border:1px solid #999999; -} - -div.cartodb-dropdown div.tail { - position:absolute; - top:-6px; - right:10px; - width:0; - height:0; - border-left:6px solid transparent; - border-right:6px solid transparent; - border-bottom:6px solid #999; - z-index:0; -} - -div.cartodb-dropdown div.tail span.border { - position:absolute; - top:1px; - left:-6px; - width:0; - height:0; - border-left:6px solid transparent; - border-right:6px solid transparent; - border-bottom:6px solid white; - z-index:2; -} - -/* Gmaps attribution */ -div#cartodb-gmaps-attribution { - position:absolute; - display:block; - bottom:13px; - right:0; - height:10px; - line-height:10px; - padding:0 6px 4px 6px; - background: white; - background: rgba(245,245,245,0.7); - font-family: "Roboto", Arial, sans-serif!important; - font-size: 11px; - font-weight: 400; - color: #444!important; - white-space: nowrap; - direction: ltr; - text-align: right; - background-position:initial initial; - background-repeat: initial initial; - border:none; - z-index:10000; -} - -div#cartodb-gmaps-attribution a { - color: #444; - text-decoration:none; -} - -/* SLIDER */ -div.cartodb-timeslider { - position: absolute; - display: inline-block; - height:40px; - width:auto!important; - margin-bottom:30px; - padding:0; - -webkit-box-shadow: rgba(0, 0, 0, 0.2) 0 0 4px 2px; - -moz-box-shadow: rgba(0, 0, 0, 0.2) 0 0 4px 2px; - box-shadow: rgba(0, 0, 0, 0.2) 0 0 4px 2px; - background: white; - -webkit-border-radius: 4px; - -moz-border-radius: 4px; - -ms-border-radius: 4px; - -o-border-radius: 4px; - border-radius: 4px; - border: 1px solid #999999; - text-align:left; - z-index:105; -} - -div.cartodb-timeslider ul { - display:block; - height:40px; - margin:0; - padding:0; - line-height:40px; - list-style:none; - cursor: default; -} - -div.cartodb-timeslider ul li { - display:inline-block; zoom: 1; *display: inline; vertical-align:top; - height:40px; - _height:40px; - width:auto; - line-height:40px; - border-right:1px solid #E5E5E5; -} - -div.cartodb-timeslider ul li.last { - border-right:none; -} - -div.cartodb-timeslider a.button { - display:block; - width:48px; - height:40px; - text-indent:-9999px; - line-height:0; - font-size:0; - background:url('../img/slider.png') no-repeat -2px -55px; -} - -div.cartodb-timeslider a.button:hover { - background-position:-42px -55px; -} - -div.cartodb-timeslider a.button.stop { - background-position:-2px -4px; -} - -div.cartodb-timeslider a.button.stop:hover { - background-position:-42px -4px; -} - -div.cartodb-timeslider p { - width:75px; - height:40px; - margin:0; - padding:0 5px 0 0; - line-height:40px; - font-size:13px; - font-weight:bold; - font-family: 'Helvetica',Arial; - text-align:center; - color:#999999; -} - -.cartodb-header { - display:none; - position:relative; - width: 100%; - background-color: rgba(0, 0, 0, 0.5); - font-family: 'Helvetica Neue', Helvetica, sans-serif; - line-height: normal; - z-index: 99999; -} -.cartodb-header .content { - padding: 10px; -} -.cartodb-header .content a { - color: #fff; -} -.cartodb-header .content a:hover { - color: #ccc; -} -.cartodb-header .content .title { - display:none; - margin: 0 0 5px 0; - line-height: normal; - font-family: 'Helvetica Neue', Helvetica, sans-serif; - font-weight: bold; - font-size:15px; - color: #fff; -} -.cartodb-header .content .description { - display:none; - font-family: 'Helvetica Neue', Helvetica, sans-serif; - line-height: normal; - color: #fff; - font-size:13px; -} -.cartodb-overlay.overlay-annotation { - display:none; -} -.cartodb-overlay.overlay-text, -.cartodb-overlay.overlay-annotation { - position:absolute; - display:none; - - -webkit-border-radius: 3px; - -moz-border-radius: 3px; - -ms-border-radius: 3px; - -o-border-radius: 3px; - border-radius: 3px; - - font-size: 20px; - line-height: normal; - color: #fff; - - -ms-word-break: break-word; - word-break: break-word; - - -webkit-hyphens: auto; - -moz-hyphens: auto; - hyphens: auto; - - z-index: 11; -} - -.cartodb-overlay.overlay-text .content, -.cartodb-overlay.overlay-annotation .content { - padding: 10px; -} - -.cartodb-overlay.overlay-text .text { - font-size: 20px; - line-height: normal; - color: #fff; - - -ms-word-break: break-word; - word-break: break-word; - - -webkit-hyphens: auto; - -moz-hyphens: auto; - hyphens: auto; -} - -.cartodb-overlay.overlay-text .text strong, -.cartodb-overlay.overlay-annotation .text strong { - font-weight: bold; -} -.cartodb-overlay.overlay-text .text em, -.cartodb-overlay.overlay-annotation .text em { - font-style: italic; -} -.cartodb-overlay.overlay-text div.text a, -.cartodb-overlay.overlay-annotation div.text a { - color: inherit; -} -.cartodb-overlay.overlay-text .text a:hover, -.cartodb-overlay.overlay-annotation .text a:hover { - color: inherit; - filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=80); - opacity: 0.8; -} -.cartodb-overlay.overlay-annotation { - -webkit-border-radius: 2px; - -moz-border-radius: 2px; - -ms-border-radius: 2px; - -o-border-radius: 2px; - border-radius: 2px; -} -.cartodb-overlay.overlay-annotation .content { - padding: 5px; -} -.cartodb-overlay.overlay-annotation.align-right .stick .ball { left: auto; right: -6px; } -.cartodb-overlay.overlay-annotation .stick { - position: absolute; - top: 50%; - left: -50px; - margin-top: -1px; - width: 50px; - height: 2px; - background: #333; -} -.cartodb-overlay.overlay-annotation .stick .ball { - position:absolute; - left: -6px; - top: 50%; - margin-top: -3px; - width: 6px; - height: 6px; - background: #333; - -webkit-border-radius: 200px; - -moz-border-radius: 200px; - -ms-border-radius: 200px; - -o-border-radius: 200px; - border-radius: 200px; -} - -.cartodb-overlay.image-overlay { - display:none; - position:absolute; - - -webkit-border-radius: 3px; - -moz-border-radius: 3px; - -ms-border-radius: 3px; - -o-border-radius: 3px; - border-radius: 3px; - - z-index: 11; -} - -.cartodb-overlay.image-overlay .content { - padding: 10px; -} - -.cartodb-overlay.image-overlay img { - display: block; -} - -@font-face { - font-family: 'Droid Sans'; - font-style: normal; - font-weight: 400; - src: local('Droid Sans'), local('DroidSans'), url(//themes.googleusercontent.com/static/fonts/droidsans/v4/s-BiyweUPV0v-yRb-cjciL3hpw3pgy2gAi-Ip7WPMi0.woff) format('woff'); -} - -@font-face { - font-family: 'Droid Sans'; - font-style: bold; - font-weight: 700; - src: local('Droid Sans Bold'), local('DroidSans-Bold'), url(//themes.googleusercontent.com/static/fonts/droidsans/v4/EFpQQyG9GqCrobXxL-KRMXbFhgvWbfSbdVg11QabG8w.woff) format('woff'); -} - -@font-face { - font-family: 'Vollkorn'; - font-style: normal; - font-weight: 400; - src: local('Vollkorn Regular'), local('Vollkorn-Regular'), url(//themes.googleusercontent.com/static/fonts/vollkorn/v4/BCFBp4rt5gxxFrX6F12DKnYhjbSpvc47ee6xR_80Hnw.woff) format('woff'); -} - -@font-face { - font-family: 'Vollkorn'; - font-style: normal; - font-weight: 400; - src: local('Vollkorn Regular'), local('Vollkorn-Regular'), url(//themes.googleusercontent.com/static/fonts/vollkorn/v4/BCFBp4rt5gxxFrX6F12DKnYhjbSpvc47ee6xR_80Hnw.woff) format('woff'); -} - -@font-face { - font-family: 'Vollkorn'; - font-style: bold; - font-weight: 700; - src: local('Vollkorn Bold'), local('Vollkorn-Bold'), url(//themes.googleusercontent.com/static/fonts/vollkorn/v4/wMZpbUtcCo9GUabw9JODerrIa-7acMAeDBVuclsi6Gc.woff) format('woff'); -} - -@font-face { - font-family: 'Open Sans'; - font-style: bold; - font-weight: 400; - src: local('Open Sans'), local('OpenSans'), url(//themes.googleusercontent.com/static/fonts/opensans/v8/cJZKeOuBrn4kERxqtaUH3bO3LdcAZYWl9Si6vvxL-qU.woff) format('woff'); -} - -@font-face { - font-family: 'Open Sans'; - font-style: bold; - font-weight: 600; - src: local('Open Sans Semibold'), local('OpenSans-Semibold'), url(//themes.googleusercontent.com/static/fonts/opensans/v8/MTP_ySUJH_bn48VBG8sNSqRDOzjiPcYnFooOUGCOsRk.woff) format('woff'); -} - -@font-face { - font-family: 'Roboto Slab'; - font-style: normal; - font-weight: 400; - src: local('Roboto Slab Regular'), local('RobotoSlab-Regular'), url(//themes.googleusercontent.com/static/fonts/robotoslab/v3/y7lebkjgREBJK96VQi37ZrrIa-7acMAeDBVuclsi6Gc.woff) format('woff'); -} - -@font-face { - font-family: 'Roboto Slab'; - font-style: bold; - font-weight: 700; - src: local('Roboto Slab Bold'), local('RobotoSlab-Bold'), url(//themes.googleusercontent.com/static/fonts/robotoslab/v3/dazS1PrQQuCxC3iOAJFEJRbnBKKEOwRKgsHDreGcocg.woff) format('woff'); -} - -/* HELVETICA */ -.cartodb-overlay.overlay-text .content > .text { font-family: 'Helvetica Neue', Helvetica, sans-serif; font-weight: 400; } -.cartodb-overlay.overlay-text .content > .text strong { font-family: 'Helvetica Neue', Helvetica, sans-serif; font-weight: 700; } - -/* DROID */ -.cartodb-overlay.overlay-text.droid .content > .text { font-family: 'Droid Sans', serif; font-weight: 400; } -.cartodb-overlay.overlay-text.droid .content > .text strong { font-family: 'Droid Sans', Helvetica, sans-serif; font-weight: 700; } - -/* ROBOTO */ -.cartodb-overlay.overlay-text.roboto .content > .text { font-family: 'Roboto Slab', serif; font-weight: 400; } -.cartodb-overlay.overlay-text.roboto .content > .text strong { font-family: 'Roboto Slab', serif; font-weight: 700; } - -/* VOLLKORN */ -.cartodb-overlay.overlay-text.vollkorn .content > .text { font-family: 'Vollkorn', serif; font-weight: 400; } -.cartodb-overlay.overlay-text.vollkorn .content > .text strong { font-family: 'Vollkorn', serif; font-weight: 700; } - -/* OPEN SANS */ -.cartodb-overlay.overlay-text.open_sans .content > .text { font-family: 'Open Sans', sans-serif; font-weight: 400; } -.cartodb-overlay.overlay-text.open_sans .content > .text strong { font-family: 'Open Sans', sans-serif; font-weight: 700; } -@media -only screen and (-webkit-min-device-pixel-ratio: 2), -only screen and ( min--moz-device-pixel-ratio: 2), -only screen and ( -o-min-device-pixel-ratio: 2/1), -only screen and ( min-device-pixel-ratio: 2), -only screen and ( min-resolution: 192dpi), -only screen and ( min-resolution: 2dppx) { - - /*div.cartodb-timeslider .ui-slider .ui-slider-handle { - top: -12px!Important; - padding: 9px 3px; - background:transparent url('../img/handle@2x.png') center center no-repeat!Important; - border:none!Important; - background-size: 11px 12px!Important; - } - - div.cartodb-mobile .toggle { - float:right; - - display:block; - width: 40px; - height: 30px; - margin: auto; - - padding: 5px 0; - - background: url('../img/burguer@2x.png') no-repeat center center white; - background-size: 18px 11px; - }*/ - -} - - -@media only screen -and (max-device-width : 320px) -and (max-device-width : 480px) { - - div.cartodb-map-wrapper div.cartodb-searchbox { - display: none; - } - - /*div.cartodb-map-wrapper div.cartodb-timeslider .ui-slider .ui-slider-handle { - top: -12px; - padding: 9px 3px; - background:transparent url('../img/handle@2x.png') center center no-repeat; - border:none; - background-size: 11px 12px; - } -*/ -} - -/* iPhone portrait */ -@media only screen -and (min-device-width : 320px) -and (max-device-width : 480px) -and (orientation : portrait) { - - /*.cartodb-text {display:none}*/ - /*.cartodb-map-wrapper { bottom: 0!Important;}*/ - -} - -/* iPhone landscape */ -@media only screen -and (min-device-width : 320px) -and (max-device-width : 480px) -and (orientation : landscape) { - - /*.cartodb-text {display:none}*/ - /*.cartodb-map-wrapper { bottom: 0!Important;}*/ - - /*.torque div.cartodb-timeslider .slider-wrapper {*/ - /*width:98px!important;*/ - /*}*/ - - /*.torque div.cartodb-timeslider .slider {*/ - /*width:98px!important;*/ - /*}*/ -} - -@media only screen and (min-width: 360px) and (max-width: 490px) { - - /*div.cartodb-timeslider .slider-wrapper {*/ - /*width:198px!important;*/ - /*}*/ - - /*div.cartodb-timeslider .slider {*/ - /*width:198px!important;*/ - /*}*/ - -} - -/* iPhone 5 portrait */ -@media screen and (device-aspect-ratio: 40/71) -and (orientation : portrait) { - /*.cartodb-text {display:none}*/ - /*.cartodb-map-wrapper { bottom: 0!Important;}*/ - /*div.cartodb-timeslider .ui-slider .ui-slider-handle {*/ - /*top: -12px!Important;*/ - /*padding: 9px 3px;*/ - /*background:transparent url('../img/handle@2x.png') center center no-repeat!Important;*/ - /*border:none!Important;*/ - /*background-size: 11px 12px!Important;*/ - /*}*/ -} - -/* iPhone 5 landscape */ -@media screen and (device-aspect-ratio: 40/71) -and (orientation : landscape) { - /*.cartodb-text {display:none}*/ - /*.cartodb-map-wrapper { bottom: 0!Important;}*/ - - /*div.cartodb-timeslider .ui-slider .ui-slider-handle {*/ - /*top: -12px!Important;*/ - /*padding: 9px 3px;*/ - /*background:transparent url('../img/handle@2x.png') center center no-repeat!Important;*/ - /*border:none!Important;*/ - /*background-size: 11px 12px!Important;*/ - /*}*/ - -} - -/* Nexus portrait */ -@media only screen -and (min-device-width : 340px) -and (max-device-width: 768px) -and (orientation : portrait) { - - /*.cartodb-text {display:none}*/ - /*.cartodb-map-wrapper { bottom: 0!Important;}*/ - - /*div.cartodb-timeslider .ui-slider .ui-slider-handle {*/ - /*top: -12px!Important;*/ - /*padding: 9px 3px;*/ - /*background:transparent url('../img/handle@2x.png') center center no-repeat!Important;*/ - /*border:none!Important;*/ - /*background-size: 11px 12px!Important;*/ - /*}*/ - - -} - -/* Nexus landscape */ -@media only screen -and (max-device-width : 768px) -and (orientation : landscape) { - - /*.cartodb-text {display:none}*/ - /*.cartodb-map-wrapper { bottom: 0!Important;}*/ - /*div.cartodb-timeslider .ui-slider .ui-slider-handle {*/ - /*top: -12px!Important;*/ - /*padding: 9px 3px;*/ - /*background:transparent url('../img/handle@2x.png') center center no-repeat!Important;*/ - /*border:none!Important;*/ - /*background-size: 11px 12px!Important;*/ - /*}*/ - - - -} - -div.cartodb-timeslider .slider-wrapper { - display:inline-block; - zoom: 1; - *display: inline; - vertical-align:top; - width:298px; - height:4px; - _height:4px; - padding:18px 15px; -} - -div.cartodb-timeslider .slider { - width:298px; - height:4px; -} - -div.cartodb-timeslider .ui-helper-hidden { - display: none; -} - -div.cartodb-timeslider .ui-helper-hidden-accessible { - border: 0; - clip: rect(0 0 0 0); - height: 1px; - margin: -1px; - overflow: hidden; - padding: 0; - position: absolute; - width: 1px; -} -div.cartodb-timeslider .ui-helper-reset { - margin: 0; - padding: 0; - border: 0; - outline: 0; - line-height: 1.3; - text-decoration: none; - font-size: 100%; - list-style: none; -} -div.cartodb-timeslider .ui-helper-clearfix:before, -div.cartodb-timeslider .ui-helper-clearfix:after { - content: ""; - display: table; - border-collapse: collapse; -} -div.cartodb-timeslider .ui-helper-clearfix:after { - clear: both; -} -div.cartodb-timeslider .ui-helper-clearfix { - min-height: 0; -} -div.cartodb-timeslider .ui-helper-zfix { - width: 100%; - height: 100%; - top: 0; - left: 0; - position: absolute; - opacity: 0; - filter:Alpha(Opacity=0); -} - -div.cartodb-timeslider .ui-front { - z-index: 100; -} - -div.cartodb-timeslider .ui-state-disabled { - cursor: default !important; -} - -div.cartodb-timeslider .ui-icon { - display: block; - text-indent: -99999px; - overflow: hidden; - background-repeat: no-repeat; -} - -div.cartodb-timeslider .ui-widget-overlay { - position: fixed; - top: 0; - left: 0; - width: 100%; - height: 100%; -} -div.cartodb-timeslider .ui-slider { - background-color: #E0E0E0; - position: relative; - text-align: left; - border-radius:2px; - -webkit-border-radius:2px; - -moz-border-radius:2px; - -o-border-radius:2px; -} -div.cartodb-timeslider .ui-slider .ui-slider-handle { - position: absolute; - z-index: 102; - width: 9px; - height: 10px; - cursor: default; - background:url('../img/slider.png') no-repeat -98px -18px white; - border:1px solid #555555; - border-radius:2px; - -webkit-border-radius:2px; - -moz-border-radius:2px; - -o-border-radius:2px; - outline:none; -} - -div.cartodb-timeslider .ui-slider .ui-slider-handle:hover { - cursor:col-resize; - background-position:-112px -18px; -} - -div.cartodb-timeslider .ui-slider .ui-slider-range { - position: absolute; - z-index: 100; - font-size: .7em; - display: block; - border: 0; - background-position: 0 0; - background-color:#397DBA; - border-radius:2px; - -webkit-border-radius:2px; - -moz-border-radius:2px; - -o-border-radius:2px; -} - -div.cartodb-timeslider .ui-slider.ui-state-disabled .ui-slider-handle, -div.cartodb-timeslider .ui-slider.ui-state-disabled .ui-slider-range { - filter: inherit; -} - -div.cartodb-timeslider .ui-slider-horizontal { - height: 4px; - cursor:pointer; -} -div.cartodb-timeslider .ui-slider-horizontal .ui-slider-handle { - top: -4px; - margin-left: -6px; -} -div.cartodb-timeslider .ui-slider-horizontal .ui-slider-range { - top: 0; - height: 100%; - cursor:pointer; -} -div.cartodb-timeslider .ui-slider-horizontal .ui-slider-range-min { - left: 0; -} -div.cartodb-timeslider .ui-slider-horizontal .ui-slider-range-max { - right: 0; -} - -div.cartodb-timeslider .ui-slider-vertical { - width: .8em; - height: 100px; -} -div.cartodb-timeslider .ui-slider-vertical .ui-slider-handle { - left: -.3em; - margin-left: 0; - margin-bottom: -.6em; -} -div.cartodb-timeslider .ui-slider-vertical .ui-slider-range { - left: 0; - width: 100%; -} -div.cartodb-timeslider .ui-slider-vertical .ui-slider-range-min { - bottom: 0; -} -div.cartodb-timeslider .ui-slider-vertical .ui-slider-range-max { - top: 0; -} -/* required styles */ - -.leaflet-map-pane, -.leaflet-tile, -.leaflet-marker-icon, -.leaflet-marker-shadow, -.leaflet-tile-pane, -.leaflet-tile-container, -.leaflet-overlay-pane, -.leaflet-shadow-pane, -.leaflet-marker-pane, -.leaflet-popup-pane, -.leaflet-overlay-pane svg, -.leaflet-zoom-box, -.leaflet-image-layer, -.leaflet-layer { - position: absolute; - left: 0; - top: 0; - } -.leaflet-container { - overflow: hidden; - -ms-touch-action: none; - } -.leaflet-tile, -.leaflet-marker-icon, -.leaflet-marker-shadow { - -webkit-user-select: none; - -moz-user-select: none; - user-select: none; - -webkit-user-drag: none; - } -.leaflet-marker-icon, -.leaflet-marker-shadow { - display: block; - } -/* map is broken in FF if you have max-width: 100% on tiles */ -.leaflet-container img { - max-width: none !important; - } -/* stupid Android 2 doesn't understand "max-width: none" properly */ -.leaflet-container img.leaflet-image-layer { - max-width: 15000px !important; - } -.leaflet-tile { - filter: inherit; - visibility: hidden; - } -.leaflet-tile-loaded { - visibility: inherit; - } -.leaflet-zoom-box { - width: 0; - height: 0; - } -/* workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=888319 */ -.leaflet-overlay-pane svg { - -moz-user-select: none; - } - -.leaflet-tile-pane { z-index: 2; } -.leaflet-objects-pane { z-index: 3; } -.leaflet-overlay-pane { z-index: 4; } -.leaflet-shadow-pane { z-index: 5; } -.leaflet-marker-pane { z-index: 6; } -.leaflet-popup-pane { z-index: 7; } - -.leaflet-vml-shape { - width: 1px; - height: 1px; - } -.lvml { - behavior: url(#default#VML); - display: inline-block; - position: absolute; - } - - -/* control positioning */ - -.leaflet-control { - position: relative; - z-index: 7; - pointer-events: auto; - } -.leaflet-top, -.leaflet-bottom { - position: absolute; - z-index: 1000; - pointer-events: none; - } -.leaflet-top { - top: 0; - } -.leaflet-right { - right: 0; - } -.leaflet-bottom { - bottom: 0; - } -.leaflet-left { - left: 0; - } -.leaflet-control { - float: left; - clear: both; - } -.leaflet-right .leaflet-control { - float: right; - } -.leaflet-top .leaflet-control { - margin-top: 10px; - } -.leaflet-bottom .leaflet-control { - margin-bottom: 10px; - } -.leaflet-left .leaflet-control { - margin-left: 10px; - } -.leaflet-right .leaflet-control { - margin-right: 10px; - } - - -/* zoom and fade animations */ - -.leaflet-fade-anim .leaflet-tile, -.leaflet-fade-anim .leaflet-popup { - opacity: 0; - -webkit-transition: opacity 0.2s linear; - -moz-transition: opacity 0.2s linear; - -o-transition: opacity 0.2s linear; - transition: opacity 0.2s linear; - } -.leaflet-fade-anim .leaflet-tile-loaded, -.leaflet-fade-anim .leaflet-map-pane .leaflet-popup { - opacity: 1; - } - -.leaflet-zoom-anim .leaflet-zoom-animated { - -webkit-transition: -webkit-transform 0.25s cubic-bezier(0,0,0.25,1); - -moz-transition: -moz-transform 0.25s cubic-bezier(0,0,0.25,1); - -o-transition: -o-transform 0.25s cubic-bezier(0,0,0.25,1); - transition: transform 0.25s cubic-bezier(0,0,0.25,1); - } -.leaflet-zoom-anim .leaflet-tile, -.leaflet-pan-anim .leaflet-tile, -.leaflet-touching .leaflet-zoom-animated { - -webkit-transition: none; - -moz-transition: none; - -o-transition: none; - transition: none; - } - -.leaflet-zoom-anim .leaflet-zoom-hide { - visibility: hidden; - } - - -/* cursors */ - -.leaflet-clickable { - cursor: pointer; - } -.leaflet-container { - cursor: -webkit-grab; - cursor: -moz-grab; - } -.leaflet-popup-pane, -.leaflet-control { - cursor: auto; - } -.leaflet-dragging .leaflet-container, -.leaflet-dragging .leaflet-clickable { - cursor: move; - cursor: -webkit-grabbing; - cursor: -moz-grabbing; - } - - -/* visual tweaks */ - -.leaflet-container { - background: #ddd; - outline: 0; - } -.leaflet-container a { - color: #0078A8; - } -.leaflet-container a.leaflet-active { - outline: 2px solid orange; - } -.leaflet-zoom-box { - border: 2px dotted #38f; - background: rgba(255,255,255,0.5); - } - - -/* general typography */ -.leaflet-container { - font: 12px/1.5 "Helvetica Neue", Arial, Helvetica, sans-serif; - } - - -/* general toolbar styles */ - -.leaflet-bar { - box-shadow: 0 1px 5px rgba(0,0,0,0.65); - border-radius: 4px; - } -.leaflet-bar a, -.leaflet-bar a:hover { - background-color: #fff; - border-bottom: 1px solid #ccc; - width: 26px; - height: 26px; - line-height: 26px; - display: block; - text-align: center; - text-decoration: none; - color: black; - } -.leaflet-bar a, -.leaflet-control-layers-toggle { - background-position: 50% 50%; - background-repeat: no-repeat; - display: block; - } -.leaflet-bar a:hover { - background-color: #f4f4f4; - } -.leaflet-bar a:first-child { - border-top-left-radius: 4px; - border-top-right-radius: 4px; - } -.leaflet-bar a:last-child { - border-bottom-left-radius: 4px; - border-bottom-right-radius: 4px; - border-bottom: none; - } -.leaflet-bar a.leaflet-disabled { - cursor: default; - background-color: #f4f4f4; - color: #bbb; - } - -.leaflet-touch .leaflet-bar a { - width: 30px; - height: 30px; - line-height: 30px; - } - - -/* zoom control */ - -.leaflet-control-zoom-in, -.leaflet-control-zoom-out { - font: bold 18px 'Lucida Console', Monaco, monospace; - text-indent: 1px; - } -.leaflet-control-zoom-out { - font-size: 20px; - } - -.leaflet-touch .leaflet-control-zoom-in { - font-size: 22px; - } -.leaflet-touch .leaflet-control-zoom-out { - font-size: 24px; - } - - -/* layers control */ - -.leaflet-control-layers { - box-shadow: 0 1px 5px rgba(0,0,0,0.4); - background: #fff; - border-radius: 5px; - } -.leaflet-control-layers-toggle { - background-image: url(images/layers.png); - width: 36px; - height: 36px; - } -.leaflet-retina .leaflet-control-layers-toggle { - background-image: url(images/layers-2x.png); - background-size: 26px 26px; - } -.leaflet-touch .leaflet-control-layers-toggle { - width: 44px; - height: 44px; - } -.leaflet-control-layers .leaflet-control-layers-list, -.leaflet-control-layers-expanded .leaflet-control-layers-toggle { - display: none; - } -.leaflet-control-layers-expanded .leaflet-control-layers-list { - display: block; - position: relative; - } -.leaflet-control-layers-expanded { - padding: 6px 10px 6px 6px; - color: #333; - background: #fff; - } -.leaflet-control-layers-selector { - margin-top: 2px; - position: relative; - top: 1px; - } -.leaflet-control-layers label { - display: block; - } -.leaflet-control-layers-separator { - height: 0; - border-top: 1px solid #ddd; - margin: 5px -10px 5px -6px; - } - - -/* attribution and scale controls */ - -.leaflet-container .leaflet-control-attribution { - background: #fff; - background: rgba(255, 255, 255, 0.7); - margin: 0; - } -.leaflet-control-attribution, -.leaflet-control-scale-line { - padding: 0 5px; - color: #333; - } -.leaflet-control-attribution a { - text-decoration: none; - } -.leaflet-control-attribution a:hover { - text-decoration: underline; - } -.leaflet-container .leaflet-control-attribution, -.leaflet-container .leaflet-control-scale { - font-size: 11px; - } -.leaflet-left .leaflet-control-scale { - margin-left: 5px; - } -.leaflet-bottom .leaflet-control-scale { - margin-bottom: 5px; - } -.leaflet-control-scale-line { - border: 2px solid #777; - border-top: none; - line-height: 1.1; - padding: 2px 5px 1px; - font-size: 11px; - white-space: nowrap; - overflow: hidden; - -moz-box-sizing: content-box; - box-sizing: content-box; - - background: #fff; - background: rgba(255, 255, 255, 0.5); - } -.leaflet-control-scale-line:not(:first-child) { - border-top: 2px solid #777; - border-bottom: none; - margin-top: -2px; - } -.leaflet-control-scale-line:not(:first-child):not(:last-child) { - border-bottom: 2px solid #777; - } - -.leaflet-touch .leaflet-control-attribution, -.leaflet-touch .leaflet-control-layers, -.leaflet-touch .leaflet-bar { - box-shadow: none; - } -.leaflet-touch .leaflet-control-layers, -.leaflet-touch .leaflet-bar { - border: 2px solid rgba(0,0,0,0.2); - background-clip: padding-box; - } - - -/* popup */ - -.leaflet-popup { - position: absolute; - text-align: center; - } -.leaflet-popup-content-wrapper { - padding: 1px; - text-align: left; - border-radius: 12px; - } -.leaflet-popup-content { - margin: 13px 19px; - line-height: 1.4; - } -.leaflet-popup-content p { - margin: 18px 0; - } -.leaflet-popup-tip-container { - margin: 0 auto; - width: 40px; - height: 20px; - position: relative; - overflow: hidden; - } -.leaflet-popup-tip { - width: 17px; - height: 17px; - padding: 1px; - - margin: -10px auto 0; - - -webkit-transform: rotate(45deg); - -moz-transform: rotate(45deg); - -ms-transform: rotate(45deg); - -o-transform: rotate(45deg); - transform: rotate(45deg); - } -.leaflet-popup-content-wrapper, -.leaflet-popup-tip { - background: white; - - box-shadow: 0 3px 14px rgba(0,0,0,0.4); - } -.leaflet-container a.leaflet-popup-close-button { - position: absolute; - top: 0; - right: 0; - padding: 4px 4px 0 0; - text-align: center; - width: 18px; - height: 14px; - font: 16px/14px Tahoma, Verdana, sans-serif; - color: #c3c3c3; - text-decoration: none; - font-weight: bold; - background: transparent; - } -.leaflet-container a.leaflet-popup-close-button:hover { - color: #999; - } -.leaflet-popup-scrolled { - overflow: auto; - border-bottom: 1px solid #ddd; - border-top: 1px solid #ddd; - } - -.leaflet-oldie .leaflet-popup-content-wrapper { - zoom: 1; - } -.leaflet-oldie .leaflet-popup-tip { - width: 24px; - margin: 0 auto; - - -ms-filter: "progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678)"; - filter: progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678); - } -.leaflet-oldie .leaflet-popup-tip-container { - margin-top: -1px; - } - -.leaflet-oldie .leaflet-control-zoom, -.leaflet-oldie .leaflet-control-layers, -.leaflet-oldie .leaflet-popup-content-wrapper, -.leaflet-oldie .leaflet-popup-tip { - border: 1px solid #999; - } - - -/* div icon */ - -.leaflet-div-icon { - background: #fff; - border: 1px solid #666; - } - - /** - * CartoDB tooltip dark styles - */ - - div.cartodb-tooltip-content-wrapper.dark { - background: rgb(0,0,0); - background:rgba(0,0,0,0.75); - filter:progid:DXImageTransform.Microsoft.gradient(startColorstr=#bf000000, endColorstr=#bf000000); - -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorstr=#bf000000, endColorstr=#bf000000)"; - } - - div.cartodb-tooltip-content-wrapper.dark h4 { - color:#999; - } - - div.cartodb-tooltip-content-wrapper.dark p { - color:#FFFFFF; - } - - div.cartodb-tooltip-content-wrapper.dark a { - color:#397DB9; - } - /** - * CartoDB2.0 tooltip styles (DEFAULT) - */ - - div.cartodb-tooltip { - position: absolute; - display: none; - min-width:120px; - max-width:180px; - overflow-y:hidden; - z-index: 5; - } - - div.cartodb-tooltip-content-wrapper { - -webkit-border-radius: 2px; - -moz-border-radius: 2px; - border-radius: 2px; - background: rgb(255,255,255); - background: rgba(255,255,255,0.9); - filter:progid:DXImageTransform.Microsoft.gradient(startColorstr=#E5FFFFFF, endColorstr=#E5FFFFFF); - -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorstr=#E5FFFFFF, endColorstr=#E5FFFFFF)"; - zoom: 1; - } - - div.cartodb-tooltip-content { - display:block; - padding:8px 8px 8px 9px; - } - - div.cartodb-tooltip-content h4 { - display:block; - text-transform: uppercase; - font:normal 10px "Helvetica Neue","Helvetica",Arial; - color:#AAA; - word-wrap: break-word; - } - - div.cartodb-tooltip-content p { - display:block; - margin:0; - padding:0 0 7px; - font:normal 12px "Helvetica Neue", "Helvetica", Arial; - color:#333333; - word-wrap: break-word; - } - - div.cartodb-tooltip-content p:last-child { - padding:0; - } - - div.cartodb-tooltip-content a { - color:#0078A8; - } - - - /* Old tooltip styles */ - div.cartodb-tooltip > p { - font-family: "robotoregular", Helvetica, Arial, Sans-serif; - font-size: 15px; - color: #333; - text-align:center; - text-shadow: -1px -1px 0 #FFF, 1px -1px 0 #FFF, -1px 1px 0 #FFF, 1px 1px 0 #FFF; - } - - /** - * CartoDB ooltip light styles - */ - - \ No newline at end of file