Compare commits

..

No commits in common. "develop" and "v2.6" have entirely different histories.

734 changed files with 40625 additions and 57025 deletions

View file

@ -1 +0,0 @@
app/assets/javascripts/metamaps.secret.bundle.js

View file

@ -1,10 +0,0 @@
{
"presets": [
"react",
"es2015"
],
"plugins": [
"lodash",
"transform-class-properties"
]
}

View file

@ -1,2 +1 @@
https://github.com/heroku/heroku-buildpack-nodejs.git
https://github.com/heroku/heroku-buildpack-ruby.git

View file

@ -1,38 +0,0 @@
---
engines:
brakeman:
enabled: true
bundler-audit:
enabled: true
duplication:
enabled: true
config:
languages:
count_threshold: 3 # rule of three
ruby:
mass_threshold: 36 # default: 18
javascript:
mass_threshold: 80 # default: 40
eslint:
enabled: true
channel: "eslint-3"
fixme:
enabled: true
rubocop:
enabled: true
exclude_fingerprints:
- 74f18007b920e8d81148d2f6a2756534
ratings:
paths:
- 'Gemfile.lock'
- '**.erb'
- '**.rb'
- '**.js'
- '**.jsx'
exclude_paths:
- app/assets/images/
- app/assets/javascripts/lib/
- frontend/src/patched/
- db/
- script/
- spec/

View file

@ -1,3 +0,0 @@
**/*{.,-}min.js
frontend/src/patched/*
app/assets/javascripts/lib/*

View file

@ -1,26 +0,0 @@
module.exports = {
"sourceType": "module",
"parser": "babel-eslint",
"parserOptions": {
"ecmaFeatures": {
"jsx": true
}
},
"extends": "standard",
"installedESLint": true,
"env": {
"es6": true,
"node": true
},
"plugins": [
"promise",
"standard",
"react"
],
"rules": {
"react/jsx-uses-react": [2],
"react/jsx-uses-vars": [2],
"space-before-function-paren": [2, "never"],
"yoda": [2, "never", { "exceptRange": true }]
}
}

View file

@ -1,56 +0,0 @@
# Node JS env
export NODE_REALTIME_PORT='5000' # should match REALTIME_SERVER, below
# Rails env
export DB_USERNAME='postgres'
export DB_PASSWORD='3112'
export DB_HOST='localhost'
export DB_PORT='5432'
export DB_NAME='metamaps'
export REALTIME_SERVER='http://localhost:5000'
export MAILER_DEFAULT_URL='localhost:3000'
export DEVISE_MAILER_SENDER='team@metamaps.cc'
export DEVISE_SECRET_KEY='f71c467e526f23d614b3b08866cad4788c502bed869c282f06e73ee6c94675b62fe1f6d52fa7ba8196b33031f0d2f3b67e27ea07693c52ecebccb01700cad614'
export SECRET_KEY_BASE='267c8a84f63963282f45bc3010eaddf027abfab58fc759d6e239c8005f85ee99d6d01b1ab6394cdee9ca7f8c9213a0cf91d3d8d3350f096123e2caccbcc0924f'
# # you can safely leave these blank, unless you're deploying an instance, in
# # which case you'll need to set them up
#
# export S3_REGION
# export S3_BUCKET_NAME
# export AWS_ACCESS_KEY_ID
# export AWS_SECRET_ACCESS_KEY
#
# export SMTP_DOMAIN
# export SMTP_PASSWORD
# export SMTP_PORT
# export SMTP_SERVER
# export SMTP_USERNAME
# # send exception notifications to a slack incoming webhook
# export SLACK_EN_WEBHOOK_URL
# ruby garbage collection stuff
export RUBY_GC_TUNE=0 #set to 1 to enable GC test
export RUBY_GC_TOKEN=4f4380fc9a2857d1f008005a3eb86928
export RUBY_GC_HEAP_INIT_SLOTS=186426
export RUBY_GC_HEAP_FREE_SLOTS=559278
export RUBY_GC_HEAP_GROWTH_FACTOR=1.03
export RUBY_GC_HEAP_GROWTH_MAX_SLOTS=74570
export RUBY_GC_HEAP_OLDOBJECT_LIMIT_FACTOR=1.4
export RUBY_GC_MALLOC_LIMIT=32883406
export RUBY_GC_MALLOC_LIMIT_MAX=69055153
export RUBY_GC_MALLOC_LIMIT_GROWTH_FACTOR=1.68
export RUBY_GC_OLDMALLOC_LIMIT=32509481
export RUBY_GC_OLDMALLOC_LIMIT_MAX=68269910
export RUBY_GC_OLDMALLOC_LIMIT_GROWTH_FACTOR=1.4
## find the ENV currently in use in the app using :
## grep -rIso -P "(?<=ENV)(\.fetch\(|\[).[A-Z_]+.(\)|\])"
# for a uniq ordered list of env vars:
## grep -rIsoh -P "(?<=ENV)(\.fetch\(|\[).[A-Z_]+.(\)|\])" | grep -oP "[A-Z_]+" | sort -u > temp

26
.gitignore vendored
View file

@ -4,32 +4,20 @@
# or operating system, you probably want to add a global ignore instead:
# git config --global core.excludesfile ~/.gitignore_global
#assety stuff
public/assets
public/metamaps_mobile
public/api/index.html
vendor/
node_modules
npm-debug.log
app/assets/javascripts/webpacked
#secrets and config
.env
*.swp
realtime/node_modules
config/database.yml
#public/assets
# Ignore bundler config
.bundle
# Ignore the default SQLite database.
db/*.sqlite3
# Ignore all logfiles and tempfiles.
log/*.log
tmp
.tmp
coverage
.DS_Store
*/.DS_Store
.DS_Store?
.vagrant
gentle/
startserver.sh

1
.nvmrc
View file

@ -1 +0,0 @@
6.2.2

2
.rspec
View file

@ -1,2 +0,0 @@
--color
--require spec_helper

View file

@ -1,29 +0,0 @@
AllCops:
TargetRubyVersion: 2.3
Exclude:
- 'db/**/*'
- 'tmp/**/*'
- 'bin/**/*'
- 'vendor/**/*'
- 'app/assets/javascripts/node_modules/**/*'
- 'Vagrantfile'
Rails:
Enabled: true
Metrics/LineLength:
Max: 120
Metrics/AbcSize:
Max: 16
Style/Documentation:
Enabled: false
Style/EmptyMethod:
EnforcedStyle: expanded
# I like this cop, but occasionally code is more readable without a guard clause,
# and I don't want to write rubocop:disable comments every time that happens
Style/GuardClause:
Enabled: false

View file

@ -1 +0,0 @@
metamaps

View file

@ -1 +1 @@
2.3.0
2.1.3

View file

@ -1,7 +0,0 @@
if ENV['COVERAGE'] == 'on'
SimpleCov.start 'rails' do
add_group 'Policies', 'app/policies'
add_group 'Services', 'app/services'
add_group 'Serializers', 'app/serializers'
end
end

View file

@ -1,25 +0,0 @@
sudo: false
language: ruby
cache:
bundler: true
directories:
- app/assets/javascripts/node_modules
rvm:
- 2.3.0
before_script:
- echo "Rspec setup"
- export RAILS_ENV=test
- cp .example-env .env
- bundle exec rake db:create
- bundle exec rake db:schema:load
- echo "node setup"
- . $HOME/.nvm/nvm.sh
- nvm install stable
- nvm use stable
- npm install --no-optional
script:
- bundle exec rspec && bundle exec brakeman -q -z && npm test
addons:
code_climate:
repo_token: 479d3bf56798fbc7fff3fc8151a5ed09e8ac368fd5af332c437b9e07dbebb44e
postgresql: "9.4"

93
CONTRIBUTING.md Normal file
View file

@ -0,0 +1,93 @@
# Contributing to Metamaps
Active involvement from the community is essential to help make Metamaps as beneficial for communities as it can be. You can help by reporting bugs, fixing bugs, adding features, contributing new modules and by providing feedback.
## Reporting bugs and other issues
If you think you've encountered a bug, do the following:
1. Make sure you are working with the latest version of the Metamaps `master` branch.
2. Browse through the [issues][metamaps-issues] to check if
anyone else has already reported. If someone has, feel free to add more
information to that issue to help us solve it.
3. If no one has yet submitted the issue you are encountering, add it in! Please be sure
to include as much information as possible, include errors, warnings,
screenshots, links to a video showing the problem or code that can reproduce
the issue.
## Contributing code
Metamaps is made possible by open source
contributors like you. We're very interested in getting help from the greater
community, but before you start it's important that you become acquainted with
our workflow. Following these guidelines below will make collaboration much
smoother and increase the chances that we will accept your pull request without
hiccups.
### Development Process
Our development process is very similar to the approach
described in the well-known article [A Successful Git Branching Model by Vincent
Driessen][git-branching-model]. Here's an overview:
* Our `master` branch is the branch upon which
Metamaps developers should be basing their work on. The `master` branch is not guaranteed to be stable.
* All commits intended for `master` should take place on your own personal
fork, and be submitted via pull request when ready.
* Only maintainers can accept pull requests from forks into the core Metamaps.cc
repository.
* Please squash your commits into a single commit before making a pull request.
### Getting started
1. Make sure you have a [GitHub account](https://github.com/signup/free)
2. [Fork metamaps][fork-metamaps]
3. Keep your fork up to date. Metamaps is a fast moving project, and things are
changing all the time. It's important that any changes you make are based on
the most recent version of metamaps, since it's possible that something may
have changed that breaks your pull request or invalidates its need.
4. Make sure you have a [Contributor License Agreement](http://caa.metamaps.cc) on file.
5. Read on ...
### Contributor License Agreement
Before we can accept any contributions to Metamaps, we first require that all
individuals or companies agree to our Contributor License Agreement (CLA). The e-mail
address used in the pull request will be used to check if a CLA has already been
filed, so be sure to list all email addresses that you might use to submit your
pull requests when filling it out. Our CLA can be found [here](http://caa.metamaps.cc).
### Testing and Linting
TODO
### Branch grouping tokens
All pull requests submitted to Metamaps.cc should occur on a new branch. For these
branches, we at metamaps use a short token indicating the nature of the branch in
question followed by a solidus (`/`) and a kebab-cased string describing the
branch. We are using the following tokens:
**NOTE: Not sure the above is right, but also not sure what to change it to **
bug // bug fixes
wip // work in progress
feat // feature
Bug fixes follow a [slightly different format](#bug-fixes).
### Bug fixes
If you'd like to contribute a fix for a bug you've encountered, first read up on
[how to report a bug](#reporting-bugs-and-other-issues) and report it so we are
aware of the issue. By filing the issue first, we may be able to provide you
with some insight that guides you in the right direction.
[metamaps-issues]: https://github.com/metamaps/metamaps_gen002/issues
[git-branching-model]: http://nvie.com/posts/a-successful-git-branching-model/
[fork-metamaps]: https://github.com/metamaps/metamaps_gen002/fork
[cla]: http://metamaps.cc/cla

96
Gemfile
View file

@ -1,57 +1,59 @@
# frozen_string_literal: true
source 'https://rubygems.org'
ruby '2.3.0'
ruby '2.1.3'
gem 'rails', '~> 5.0.0'
gem 'rails', '3.2.17'
# Bundle edge Rails instead:
# gem 'rails', :git => 'git://github.com/rails/rails.git'
gem 'active_model_serializers'
gem 'aws-sdk', '~> 2.7.0'
gem 'best_in_place'
gem 'delayed_job'
gem 'delayed_job_active_record'
gem 'devise'
gem 'doorkeeper'
gem 'dotenv-rails'
gem 'exception_notification'
gem 'httparty'
gem 'json'
gem 'kaminari'
gem 'mailboxer'
gem 'paperclip'
gem 'redis'
gem 'pg'
gem 'puma'
gem 'pundit'
gem 'pundit_extra'
gem 'rack-attack'
gem 'rack-cors'
gem 'redis', '~> 3.3.3'
gem 'slack-notifier'
gem 'snorlax'
gem 'sucker_punch'
gem 'cancan'
gem 'formula'
gem 'formtastic'
gem 'json'
gem 'rails3-jquery-autocomplete'
gem 'best_in_place' #in-place editing
gem 'kaminari' # pagination
gem 'uservoice-ruby'
# asset stuff
gem 'jquery-rails'
gem 'jquery-ui-rails'
gem 'sass-rails'
gem 'uglifier'
gem 'paperclip'
gem 'aws-sdk'
group :test do
gem 'brakeman', require: false
gem 'factory_bot_rails'
gem 'json-schema'
gem 'rspec-rails'
gem 'shoulda-matchers'
gem 'simplecov', require: false
#gem 'therubyracer' #optional
#gem 'rb-readline'
# Gems used only for assets and not required
# in production environments by default.
group :assets do
gem 'sass-rails'
gem 'coffee-rails', '~> 3.2.1'
# See https://github.com/sstephenson/execjs#readme for more supported runtimes
# gem 'therubyracer'
gem 'uglifier', '>= 1.0.3'
end
group :development, :test do
gem 'better_errors'
gem 'binding_of_caller'
gem 'faker'
gem 'pry-byebug'
gem 'pry-rails'
gem 'rubocop', '~> 0.48.1' # match code climate https://github.com/tootsuite/mastodon/issues/1758
gem 'timecop'
gem 'tunemygc'
group :production do #this is used on heroku
#gem 'rmagick'
end
gem 'jquery-rails', '2.1.2'
# To use ActiveModel has_secure_password
# gem 'bcrypt-ruby', '~> 3.0.0'
# To use Jbuilder templates for JSON
gem 'jbuilder', '0.8.2'
# Use unicorn as the web server
# gem 'unicorn'
# Deploy with Capistrano
# gem 'capistrano'
# To use debugger
# gem 'ruby-debug19', :require => 'ruby-debug'

View file

@ -1,350 +1,181 @@
GEM
remote: https://rubygems.org/
specs:
actioncable (5.0.5)
actionpack (= 5.0.5)
nio4r (>= 1.2, < 3.0)
websocket-driver (~> 0.6.1)
actionmailer (5.0.5)
actionpack (= 5.0.5)
actionview (= 5.0.5)
activejob (= 5.0.5)
mail (~> 2.5, >= 2.5.4)
rails-dom-testing (~> 2.0)
actionpack (5.0.5)
actionview (= 5.0.5)
activesupport (= 5.0.5)
rack (~> 2.0)
rack-test (~> 0.6.3)
rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.0, >= 1.0.2)
actionview (5.0.5)
activesupport (= 5.0.5)
builder (~> 3.1)
actionmailer (3.2.17)
actionpack (= 3.2.17)
mail (~> 2.5.4)
actionpack (3.2.17)
activemodel (= 3.2.17)
activesupport (= 3.2.17)
builder (~> 3.0.0)
erubis (~> 2.7.0)
rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.0, >= 1.0.3)
active_model_serializers (0.10.6)
actionpack (>= 4.1, < 6)
activemodel (>= 4.1, < 6)
case_transform (>= 0.2)
jsonapi-renderer (>= 0.1.1.beta1, < 0.2)
activejob (5.0.5)
activesupport (= 5.0.5)
globalid (>= 0.3.6)
activemodel (5.0.5)
activesupport (= 5.0.5)
activerecord (5.0.5)
activemodel (= 5.0.5)
activesupport (= 5.0.5)
arel (~> 7.0)
activesupport (5.0.5)
concurrent-ruby (~> 1.0, >= 1.0.2)
i18n (~> 0.7)
minitest (~> 5.1)
tzinfo (~> 1.1)
addressable (2.5.2)
public_suffix (>= 2.0.2, < 4.0)
arel (7.1.4)
ast (2.3.0)
aws-sdk (2.7.0)
aws-sdk-resources (= 2.7.0)
aws-sdk-core (2.7.0)
aws-sigv4 (~> 1.0)
jmespath (~> 1.0)
aws-sdk-resources (2.7.0)
aws-sdk-core (= 2.7.0)
aws-sigv4 (1.0.2)
bcrypt (3.1.11)
best_in_place (3.1.1)
actionpack (>= 3.2)
railties (>= 3.2)
better_errors (2.3.0)
coderay (>= 1.0.0)
erubi (>= 1.0.0)
rack (>= 0.9.0)
binding_of_caller (0.7.2)
debug_inspector (>= 0.0.1)
brakeman (3.7.2)
builder (3.2.3)
byebug (9.1.0)
carrierwave (1.1.0)
activemodel (>= 4.0.0)
activesupport (>= 4.0.0)
mime-types (>= 1.16)
case_transform (0.2)
activesupport
climate_control (0.2.0)
cocaine (0.5.8)
journey (~> 1.0.4)
rack (~> 1.4.5)
rack-cache (~> 1.2)
rack-test (~> 0.6.1)
sprockets (~> 2.2.1)
activemodel (3.2.17)
activesupport (= 3.2.17)
builder (~> 3.0.0)
activerecord (3.2.17)
activemodel (= 3.2.17)
activesupport (= 3.2.17)
arel (~> 3.0.2)
tzinfo (~> 0.3.29)
activeresource (3.2.17)
activemodel (= 3.2.17)
activesupport (= 3.2.17)
activesupport (3.2.17)
i18n (~> 0.6, >= 0.6.4)
multi_json (~> 1.0)
arel (3.0.3)
aws-sdk (1.54.0)
aws-sdk-v1 (= 1.54.0)
aws-sdk-v1 (1.54.0)
json (~> 1.4)
nokogiri (>= 1.4.4)
bcrypt (3.1.7)
bcrypt (3.1.7-x86-mingw32)
best_in_place (2.1.0)
jquery-rails
rails (~> 3.1)
builder (3.0.4)
cancan (1.6.10)
climate_control (0.0.3)
activesupport (>= 3.0)
cocaine (0.5.4)
climate_control (>= 0.0.3, < 1.0)
coderay (1.1.2)
concurrent-ruby (1.0.5)
debug_inspector (0.0.3)
delayed_job (4.1.3)
activesupport (>= 3.0, < 5.2)
delayed_job_active_record (4.1.2)
activerecord (>= 3.0, < 5.2)
delayed_job (>= 3.0, < 5)
devise (4.3.0)
coffee-rails (3.2.2)
coffee-script (>= 2.2.0)
railties (~> 3.2.0)
coffee-script (2.3.0)
coffee-script-source
execjs
coffee-script-source (1.8.0)
devise (3.4.0)
bcrypt (~> 3.0)
orm_adapter (~> 0.1)
railties (>= 4.1.0, < 5.2)
railties (>= 3.2.6, < 5)
responders
warden (~> 1.2.3)
diff-lcs (1.3)
docile (1.1.5)
doorkeeper (4.2.6)
railties (>= 4.2)
dotenv (2.2.1)
dotenv-rails (2.2.1)
dotenv (= 2.2.1)
railties (>= 3.2, < 5.2)
erubi (1.6.1)
erubis (2.7.0)
exception_notification (4.2.2)
actionmailer (>= 4.0, < 6)
activesupport (>= 4.0, < 6)
execjs (2.7.0)
factory_bot (4.8.2)
activesupport (>= 3.0.0)
factory_bot_rails (4.8.2)
factory_bot (~> 4.8.2)
railties (>= 3.0.0)
faker (1.8.4)
i18n (~> 0.5)
ffi (1.9.18)
globalid (0.4.0)
activesupport (>= 4.2.0)
httparty (0.15.6)
multi_xml (>= 0.5.2)
i18n (0.9.3)
concurrent-ruby (~> 1.0)
jmespath (1.3.1)
jquery-rails (4.3.1)
rails-dom-testing (>= 1, < 3)
railties (>= 4.2.0)
thor (>= 0.14, < 2.0)
jquery-ui-rails (6.0.1)
railties (>= 3.2.16)
json (2.1.0)
json-schema (2.8.0)
addressable (>= 2.4)
jsonapi-renderer (0.1.3)
kaminari (1.0.1)
activesupport (>= 4.1.0)
kaminari-actionview (= 1.0.1)
kaminari-activerecord (= 1.0.1)
kaminari-core (= 1.0.1)
kaminari-actionview (1.0.1)
actionview
kaminari-core (= 1.0.1)
kaminari-activerecord (1.0.1)
activerecord
kaminari-core (= 1.0.1)
kaminari-core (1.0.1)
loofah (2.0.3)
nokogiri (>= 1.5.9)
mail (2.6.6)
mime-types (>= 1.16, < 4)
mailboxer (0.15.1)
carrierwave (>= 0.5.8)
rails (>= 5.0.0)
method_source (0.8.2)
mime-types (3.1)
mime-types-data (~> 3.2015)
mime-types-data (3.2016.0521)
mimemagic (0.3.2)
mini_portile2 (2.3.0)
minitest (5.11.1)
multi_xml (0.6.0)
nio4r (2.1.0)
nokogiri (1.8.1)
mini_portile2 (~> 2.3.0)
orm_adapter (0.5.0)
paperclip (5.2.0)
activemodel (>= 4.2.0)
activesupport (>= 4.2.0)
cocaine (~> 0.5.5)
mime-types
mimemagic (~> 0.3.0)
parser (2.4.0.2)
ast (~> 2.3)
pg (0.21.0)
powerpack (0.1.1)
pry (0.10.4)
coderay (~> 1.1.0)
method_source (~> 0.8.1)
slop (~> 3.4)
pry-byebug (3.5.0)
byebug (~> 9.1)
pry (~> 0.10)
pry-rails (0.3.6)
pry (>= 0.10.4)
public_suffix (3.0.0)
puma (3.10.0)
pundit (1.1.0)
activesupport (>= 3.0.0)
pundit_extra (0.3.0)
rack (2.0.3)
rack-attack (5.0.1)
rack
rack-cors (1.0.1)
rack-test (0.6.3)
rack (>= 1.0)
rails (5.0.5)
actioncable (= 5.0.5)
actionmailer (= 5.0.5)
actionpack (= 5.0.5)
actionview (= 5.0.5)
activejob (= 5.0.5)
activemodel (= 5.0.5)
activerecord (= 5.0.5)
activesupport (= 5.0.5)
bundler (>= 1.3.0)
railties (= 5.0.5)
sprockets-rails (>= 2.0.0)
rails-dom-testing (2.0.3)
activesupport (>= 4.2.0)
nokogiri (>= 1.6)
rails-html-sanitizer (1.0.3)
loofah (~> 2.0)
railties (5.0.5)
actionpack (= 5.0.5)
activesupport (= 5.0.5)
method_source
rake (>= 0.8.7)
thor (>= 0.18.1, < 2.0)
rainbow (2.2.2)
rake
rake (12.3.0)
rb-fsevent (0.10.2)
rb-inotify (0.9.10)
ffi (>= 0.5.0, < 2)
redis (3.3.3)
responders (2.4.0)
actionpack (>= 4.2.0, < 5.3)
railties (>= 4.2.0, < 5.3)
rspec-core (3.6.0)
rspec-support (~> 3.6.0)
rspec-expectations (3.6.0)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.6.0)
rspec-mocks (3.6.0)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.6.0)
rspec-rails (3.6.1)
actionpack (>= 3.0)
activesupport (>= 3.0)
railties (>= 3.0)
rspec-core (~> 3.6.0)
rspec-expectations (~> 3.6.0)
rspec-mocks (~> 3.6.0)
rspec-support (~> 3.6.0)
rspec-support (3.6.0)
rubocop (0.48.1)
parser (>= 2.3.3.1, < 3.0)
powerpack (~> 0.1)
rainbow (>= 1.99.1, < 3.0)
ruby-progressbar (~> 1.7)
unicode-display_width (~> 1.0, >= 1.0.1)
ruby-progressbar (1.9.0)
sass (3.5.1)
sass-listen (~> 4.0.0)
sass-listen (4.0.0)
rb-fsevent (~> 0.9, >= 0.9.4)
rb-inotify (~> 0.9, >= 0.9.7)
sass-rails (5.0.6)
railties (>= 4.0.0, < 6)
sass (~> 3.1)
sprockets (>= 2.8, < 4.0)
sprockets-rails (>= 2.0, < 4.0)
tilt (>= 1.1, < 3)
shoulda-matchers (3.1.2)
activesupport (>= 4.0.0)
simplecov (0.15.0)
docile (~> 1.1.0)
json (>= 1.8, < 3)
simplecov-html (~> 0.10.0)
simplecov-html (0.10.2)
slack-notifier (2.3.1)
slop (3.6.0)
snorlax (0.1.6)
rails (> 4.1)
sprockets (3.7.1)
concurrent-ruby (~> 1.0)
rack (> 1, < 3)
sprockets-rails (3.2.1)
actionpack (>= 4.0)
activesupport (>= 4.0)
sprockets (>= 3.0.0)
sucker_punch (2.0.3)
concurrent-ruby (~> 1.0.0)
thor (0.20.0)
thread_safe (0.3.6)
tilt (2.0.8)
timecop (0.9.1)
tunemygc (1.0.69)
tzinfo (1.2.4)
thread_safe (~> 0.1)
uglifier (3.2.0)
execjs (>= 0.3.0, < 3)
unicode-display_width (1.3.0)
warden (1.2.7)
warden (~> 1.2.3)
erubis (2.7.0)
execjs (2.2.1)
ezcrypto (0.7.2)
formtastic (3.0.0)
actionpack (>= 3.2.13)
formula (1.0.1)
rails (> 3.0.0)
hike (1.2.3)
i18n (0.6.11)
jbuilder (0.8.2)
activesupport (>= 3.0.0)
journey (1.0.4)
jquery-rails (2.1.2)
railties (>= 3.1.0, < 5.0)
thor (~> 0.14)
json (1.8.1)
kaminari (0.16.1)
actionpack (>= 3.0.0)
activesupport (>= 3.0.0)
mail (2.5.4)
mime-types (~> 1.16)
treetop (~> 1.4.8)
mime-types (1.25.1)
mini_portile (0.6.0)
multi_json (1.10.1)
nokogiri (1.6.3.1)
mini_portile (= 0.6.0)
nokogiri (1.6.3.1-x86-mingw32)
mini_portile (= 0.6.0)
oauth (0.4.7)
orm_adapter (0.5.0)
paperclip (4.2.0)
activemodel (>= 3.0.0)
activesupport (>= 3.0.0)
cocaine (~> 0.5.3)
mime-types
pg (0.17.1)
pg (0.17.1-x86-mingw32)
polyglot (0.3.5)
rack (1.4.5)
rack-cache (1.2)
rack (>= 0.4)
rack-ssl (1.3.4)
rack
rack-test (0.6.2)
rack (>= 1.0)
rails (3.2.17)
actionmailer (= 3.2.17)
actionpack (= 3.2.17)
activerecord (= 3.2.17)
activeresource (= 3.2.17)
activesupport (= 3.2.17)
bundler (~> 1.0)
railties (= 3.2.17)
rails3-jquery-autocomplete (1.0.14)
rails (>= 3.0)
railties (3.2.17)
actionpack (= 3.2.17)
activesupport (= 3.2.17)
rack-ssl (~> 1.3.2)
rake (>= 0.8.7)
rdoc (~> 3.4)
thor (>= 0.14.6, < 2.0)
rake (10.3.2)
rdoc (3.12.2)
json (~> 1.4)
redis (3.1.0)
responders (1.1.1)
railties (>= 3.2, < 4.2)
sass (3.4.5)
sass-rails (3.2.6)
railties (~> 3.2.0)
sass (>= 3.1.10)
tilt (~> 1.3)
sprockets (2.2.2)
hike (~> 1.2)
multi_json (~> 1.0)
rack (~> 1.0)
tilt (~> 1.1, != 1.3.0)
thor (0.19.1)
thread_safe (0.3.4)
tilt (1.4.1)
treetop (1.4.15)
polyglot
polyglot (>= 0.3.1)
tzinfo (0.3.41)
uglifier (2.5.3)
execjs (>= 0.3.0)
json (>= 1.8.0)
uservoice-ruby (0.0.11)
ezcrypto (>= 0.7.2)
json (>= 1.7.5)
oauth (>= 0.4.7)
warden (1.2.3)
rack (>= 1.0)
websocket-driver (0.6.5)
websocket-extensions (>= 0.1.0)
websocket-extensions (0.1.2)
PLATFORMS
ruby
x86-mingw32
DEPENDENCIES
active_model_serializers
aws-sdk (~> 2.7.0)
aws-sdk
best_in_place
better_errors
binding_of_caller
brakeman
delayed_job
delayed_job_active_record
cancan
coffee-rails (~> 3.2.1)
devise
doorkeeper
dotenv-rails
exception_notification
factory_bot_rails
faker
httparty
jquery-rails
jquery-ui-rails
formtastic
formula
jbuilder (= 0.8.2)
jquery-rails (= 2.1.2)
json
json-schema
kaminari
mailboxer
paperclip
pg
pry-byebug
pry-rails
puma
pundit
pundit_extra
rack-attack
rack-cors
rails (~> 5.0.0)
redis (~> 3.3.3)
rspec-rails
rubocop (~> 0.48.1)
rails (= 3.2.17)
rails3-jquery-autocomplete
redis
sass-rails
shoulda-matchers
simplecov
slack-notifier
snorlax
sucker_punch
timecop
tunemygc
uglifier
RUBY VERSION
ruby 2.3.0p0
BUNDLED WITH
1.16.1
uglifier (>= 1.0.3)
uservoice-ruby

42
MacInstallation.md Normal file
View file

@ -0,0 +1,42 @@
If you are doing an upgrade and or recent pull for changes you will need to change your default ruby package from 2.1.1 to ruby 2.1.2
install homebrew
\curl -sSL https://get.rvm.io | bash -s stable --rails
rvm install 2.1.3 --with-gcc=clang
rvm use 2.1.3
gem install lunchy
(http://www.moncefbelyamani.com/how-to-install-postgresql-on-a-mac-with-homebrew-and-lunchy/)
brew install postgresql
ln -sfv /usr/local/opt/postgresql/*.plist ~/Library/LaunchAgents
createuser metamaps -P -s -d
set a password
lunchy start postgres
cd into the metamaps directory
bundle install
copy the database.yml.default file and rename it database.yml
make sure the username and password match the POSTGRES username and password you set
http://nodejs.org/ hit install, download, open, install
rake db:create
rake db:schema:load
rake db:fixtures:load
rails s
( to start the server)

View file

@ -1,3 +1 @@
web: bundle exec puma -p $PORT
worker: bundle exec rake jobs:work
web: bundle exec rails server -p $PORT

View file

@ -1,62 +1,77 @@
Metamaps
=======
[![Build Status](https://travis-ci.org/metamaps/metamaps.svg?branch=develop)](https://travis-ci.org/metamaps/metamaps)
[![Code Climate](https://codeclimate.com/github/metamaps/metamaps/badges/gpa.svg)](https://codeclimate.com/github/metamaps/metamaps)
[![Join the chat at https://gitter.im/metamaps/metamaps_gen002](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/metamaps/metamaps_gen002?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
## What is Metamaps?
Welcome to the Metamaps GitHub repo.
Metamaps is a free and open-source technology for changemakers, innovators, educators and students. It enables individuals and communities to build and visualize their shared knowledge and unlock their collective intelligence.
## About
You can find a version of this software running at [metamaps.cc][site-beta], where the technology is being tested in an open beta.
Metamaps is a free and AGPL open source technology for changemakers, innovators, educators and students. It enables individuals and communities to build and visualize their shared knowledge and unlock their collective intelligence. You can find out about more about the project at the [blog][site-blog].
Metamaps is developed and maintained by a distributed, nomadic community comprised of technologists, artists and storytellers. You can get in touch by using whichever of these channels you prefer:
You can find a version of this software running at [metamaps.cc][site-beta], where the technology is being tested in a private beta.
## How do I learn more?
- Contact: [team@metamaps.cc](mailto:team@metamaps.cc) or [@metamapps](https://twitter.com/metamapps) on Twitter
- User Documentation: [docs.metamaps.cc](https://docs.metamaps.cc)
- User Community: [hylo.com/c/metamaps](https://www.hylo.com/c/metamaps)
- To see what we're developing, or to weigh in on what you'd like to see developed, see our [Metamaps Feedback and Features](https://trello.com/b/uFOA6a2x/metamaps-feedback-feature-ideas-requests) board on trello
- To follow along with, or contribute,to our design process, see our [Metamaps Design](https://trello.com/b/8HlCikOX/metamaps-design) board on trello
- To follow along with, or contribute to, our development process, see our [Github Issues and Pull Requests](https://github.com/metamaps/metamaps/issues)
- Request an invite to the open beta [here](https://metamaps.cc/request)
Metamaps is created and maintained by a distributed, nomadic community comprised of technologists, artists and storytellers. You can get in touch with us at team@metamaps.cc or @metamapps on twitter.
<!-- markdown hack to split two lists -->
To get connected with the community interested in Metamaps, join our [Google+ community][community].
## Installation
If you are on Mac or Ubuntu you can use the following instructions to quickly get a local copy of metamaps up and running using a Vagrant virtualbox. Don't be intimidated, it's easy!
```
git clone git@github.com:metamaps/metamaps_gen002.git
```
Now ensure you have VirtualBox and Vagrant installed on your computer
```
cd metamaps_gen002
./configure.sh
```
This will do all the setup steps to make Metamaps work with a bit of behind the scenes ninja magick.
To start servers which will run metamaps you can then run:
```
./bin/start
```
To stop them:
```
./bin/stop
```
With your webservers running, open a web browser and go to `http://localhost:3000`
You can sign in with the default account
email: `user@user.com`
password: `toolsplusconsciousness`
OR create a new account at `/join`, and use access code `qwertyui`
Start mapping and programming!
While we are still figuring out vagrant for Windows, there is an older set of instructions below
- [For Windows][windows-installation]
## Contributing
Cloning this repository directly is primarily for those wishing to contribute to our codebase. Check out our [contributing instructions][contributing] to get involved.
## Community
- To send us a personal message get in touch with us via email, Twitter, or Hylo
- If you would like to report a bug, please check the [issues][contributing-issues] section in our [contributing instructions][contributing].
- If you would like to get set up as a developer, that's great! Read on for help getting your development environment set up.
## Installation for local use or development of Metamaps
First off is getting the code downloaded to your computer. You can download a zip file from github, but if you've got `git` you can just run `git clone https://github.com/metamaps/metamaps` in your terminal.
There are instructions for setup on various platforms, with particular support for Mac and Ubuntu, which can be found here:
- [Mac Install Walkthrough][mac-installation]
- [Ubuntu Install Walkthrough][ubuntu-installation]
If you prefer to isolate your install in a virtual machine, you may find it simpler to setup using Vagrant:
- [Vagrant installation][vagrant-installation]
We don't promise support for Windows, but at one point we had it running and we've kept those docs available for reference
- [Outdated Windows Walkthrough][windows-installation]
- To participate in discussions and a public forum about Metamaps, join the [Google+ community][community]
- For contributors, read more instructions in [CONTRIBUTING.md][contributing].
## Licensing information
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or(at your option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
The license can be read [here][license].
Copyright (c) 2017 Connor Turland
Copyright (c) 2015 Connor Turland
[site-blog]: http://blog.metamaps.cc
[site-beta]: http://metamaps.cc
[license]: https://github.com/metamaps/metamaps/blob/develop/LICENSE
[contributing]: https://github.com/metamaps/metamaps/blob/develop/doc/CONTRIBUTING.md
[contributing-issues]: https://github.com/metamaps/metamaps/blob/develop/doc/CONTRIBUTING.md#reporting-bugs-and-other-issues
[mac-installation]: https://github.com/metamaps/metamaps/blob/develop/doc/MacInstallation.md
[ubuntu-installation]: https://github.com/metamaps/metamaps/blob/develop/doc/UbuntuInstallation.md
[vagrant-installation]: https://github.com/metamaps/metamaps/blob/develop/doc/VagrantInstallation.md
[windows-installation]: https://github.com/metamaps/metamaps/blob/develop/doc/WindowsInstallation.md
[community]: https://plus.google.com/u/0/communities/115060009262157699234
[license]: https://github.com/metamaps/metamaps_gen002/blob/master/LICENSE
[contributing]: https://github.com/metamaps/metamaps_gen002/blob/master/CONTRIBUTING.md
[contributing-issues]: https://github.com/metamaps/metamaps_gen002/blob/master/CONTRIBUTING.md#reporting-bugs-and-other-issues
[windows-installation]: https://github.com/metamaps/metamaps_gen002/blob/master/WindowsInstallation.md

2
Rakefile Executable file → Normal file
View file

@ -1,6 +1,4 @@
#!/usr/bin/env rake
# frozen_string_literal: true
# Add your own tasks in files placed in lib/tasks ending in .rake,
# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.

114
UbuntuInstallation.md Normal file
View file

@ -0,0 +1,114 @@
Firstly this walkthrough is done with a 14.04 32bit install of Ubuntu.
All commands that I could are terminal based.
Lets check if all updates for the system are installed first
in terminal type
sudo apt-get update
now we need to install git
sudo apt-get install git
lets get our RVM installed (Ruby Version Manager) now this is fun because the package you will get from apt-get is outdated.
so we are going to use CURL to get RVM
sudo apt-get install curl
then lets install RVM with curl like this
curl -L get.rvm.io | bash -s stable
PATH=$PATH:$HOME/.rvm/bin
[[ -s "$HOME/.profile" ]] && source "$HOME/.profile"
[[ -s "$HOME/.rvm/scripts/rvm" ]] && source "$HOME/.rvm/scripts/rvm"
source ~/.rvm/scripts/rvm
now we can actually install RVM
rvm requirements
running this will check your system for requirements as well so you will need to put your system password in.
alright now we can download metamaps from the master using git
git clone https://github.com/metamaps/metamaps_gen002.git
now there is a couple other things we are going to need which is nodejs, postgresql, libpq-dev and redis-server
sudo apt-get install nodejs
sudo apt-get install postgresql
sudo apt-get install libpq-dev
sudo apt-get install redis-server
Furthermore, if you want to be able to work on profile picture uploading, or use it
you'll need ImageMagick. On Ubuntu, you can just go find ImageMagick in the Ubuntu Software Centre
Install the specific version of ruby needed this will take some time
rvm install ruby-2.1.3
Now we also need to rename your database file which is in ./config/database.default.yml to database.yml
now run inside your metamaps_gen002 folder
bundle install
in your top lvl directory for metamaps this is a lengthy process so you might want to go and make a coffee or something :)
alright now we need to make sure your postgres password is the same as it is listed in the DB file so we are going to set it by
sudo -u postgres psql
Select postgres like this
\password postgres
set the password to 3112
Then to quit
\q
now we can run the rake install and db creation
rake db:create
rake db:schema:load
rake db:fixtures:load
Execute the server:
rails s
and dont forget to run the other server for realtime...
open a new terminal
navigate to ./realtime and run
sudo apt-get install npm
npm install
nodejs realtime-server.js
Now you're all set enjoy your personal server of metamaps :)
Navigate your browser to localhost:3000 once you have the server running
Sign in with the default account
email: user@user.com
password: toolsplusconsciousness
OR create a new account at /join, and use access code 'qwertyui'
Start mapping and programming!

17
Vagrantfile vendored
View file

@ -9,13 +9,14 @@ sudo apt-get install git curl -y
# rvm and ruby
su - vagrant -c 'curl -sSL https://rvm.io/mpapis.asc | gpg --import -'
su - vagrant -c 'curl -sSL https://get.rvm.io | bash -s stable --ruby=2.3.0'
su - vagrant -c 'curl -sSL https://get.rvm.io | bash -s stable --ruby=2.1.3'
# install some other deps
sudo apt-get install nodejs -y
sudo apt-get install npm -y
sudo apt-get install postgresql -y
sudo apt-get install libpq-dev -y
sudo apt-get install redis-server -y
# get imagemagick
sudo apt-get install imagemagick --fix-missing
@ -31,15 +32,15 @@ sudo -u postgres psql -c "ALTER USER postgres WITH PASSWORD '3112';"
SCRIPT
VAGRANTFILE_API_VERSION = '2'
VAGRANTFILE_API_VERSION = "2"
Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
config.vm.box = 'trusty64'
config.vm.box_url = 'http://cloud-images.ubuntu.com/vagrant/trusty/current/trusty-server-cloudimg-amd64-vagrant-disk1.box'
config.vm.box = "trusty64"
config.vm.box_url = "http://cloud-images.ubuntu.com/vagrant/trusty/current/trusty-server-cloudimg-amd64-vagrant-disk1.box"
config.vm.network :forwarded_port, guest: 3000, host: 3000
config.vm.network :forwarded_port, guest: 5000, host: 5000
config.vm.network 'private_network', ip: '10.0.1.11'
config.vm.synced_folder '.', '/vagrant', nfs: true
config.vm.network :forwarded_port, guest: 5001, host: 5001
config.vm.network "private_network", ip: "10.0.1.11"
config.vm.synced_folder ".", "/vagrant", :nfs => true
config.vm.provision 'shell', inline: $script
config.vm.provision "shell", inline: $script
end

49
WindowsInstallation.md Normal file
View file

@ -0,0 +1,49 @@
First off, Metamaps runs on Ruby On Rails. Ruby 2.1.3 and Rails 3.2. You'll need to get Ruby and Rails installed on your computer if you don't already have it. Go to here for Ruby http://rubyinstaller.org/downloads/
You'll also need GIT: http://git-scm.com/download/win
It uses postgreSQL 9.2 as a database. You can install that for your computer from here: http://www.enterprisedb.com/products-services-training/pgdownload . During installation you can choose whatever database password you like. Make sure to note it down!
Once you install those, open a 'command prompt with ruby'.
to install rails
gem install rails -v 3.2
also download node.js, which is also needed http://nodejs.org/download/
Navigate to the folder that you want to download the metamaps files to and run the following: (use your forked git repository address if it's different than this repo. You will also need to go to your Github account settings and add the SSH key that was placed in your clipboard earlier)
git clone https://github.com/metamaps/metamaps_gen002.git --branch develop
cd metamaps_gen002
Now you're in the main directory.
Install all the gems needed for Metamaps by running
bundle install
Setting up the database:
1) Copy /config/database.yml.default and rename the copy to /config/database.yml then edit database.yml with your text editor and set the password to whatever you chose when you set up the PostGres database.
2) In a terminal:
rake db:create
rake db:schema:load
rake db:fixtures:load
Running the server:
rails s
Navigate your browser to localhost:3000 once you have the server running
Sign in with the default account
email: user@user.com
password: toolsplusconsciousness
OR create a new account at /join, and use access code 'qwertyui'
Start mapping and programming!

View file

@ -1,7 +0,0 @@
// eslint-disable spaced-comment
// JS and CSS bundles
//= link_directory ../javascripts .js
//= link_directory ../stylesheets .css
// Other
//= link_tree ../images

BIN
app/assets/images/.DS_Store vendored Normal file

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 322 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 543 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 854 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 780 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 466 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 220 B

BIN
app/assets/images/exploremaps_sprite.png Normal file → Executable file

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 223 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 667 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 822 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 418 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 717 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 739 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 331 B

BIN
app/assets/images/user_sprite.png Normal file → Executable file

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 421 B

View file

@ -1,21 +0,0 @@
/* global Metamaps */
/* erb variables from rails */
Metamaps.ServerData = Metamaps.ServerData || {}
Metamaps.ServerData['junto_spinner_darkgrey.gif'] = '<%= asset_path('junto_spinner_darkgrey.gif') %>'
Metamaps.ServerData['user.png'] = '<%= asset_path('user.png') %>'
Metamaps.ServerData['icons/wildcard.png'] = '<%= asset_path('icons/wildcard.png') %>'
Metamaps.ServerData['topic_description_signifier.png'] = '<%= asset_path('topic_description_signifier.png') %>'
Metamaps.ServerData['topic_link_signifier.png'] = '<%= asset_path('topic_link_signifier.png') %>'
Metamaps.ServerData['synapse16.png'] = '<%= asset_path('synapse16.png') %>'
Metamaps.ServerData['sounds/MM_sounds.mp3'] = '<%= asset_path 'sounds/MM_sounds.mp3' %>'
Metamaps.ServerData['sounds/MM_sounds.ogg'] = '<%= asset_path 'sounds/MM_sounds.ogg' %>'
Metamaps.ServerData['exploremaps_sprite.png'] = '<%= asset_path 'exploremaps_sprite.png' %>'
Metamaps.ServerData['map_control_sprite.png'] = '<%= asset_path 'map_control_sprite.png' %>'
Metamaps.ServerData['user_sprite.png'] = '<%= asset_path 'user_sprite.png' %>'
Metamaps.ServerData.Metacodes = <%= Metacode.all.to_json.gsub(%r[(icon.*?)(\"},)], '\1?purple=stupid\2').html_safe %>
Metamaps.ServerData.REALTIME_SERVER = '<%= ENV['REALTIME_SERVER'] %>'
Metamaps.ServerData.RAILS_ENV = '<%= ENV['RAILS_ENV'] %>'
Metamaps.ServerData.VERSION = '<%= METAMAPS_VERSION %>'
Metamaps.ServerData.BUILD = '<%= METAMAPS_BUILD %>'
Metamaps.ServerData.LAST_UPDATED = '<%= METAMAPS_LAST_UPDATED %>'

View file

@ -1,23 +0,0 @@
// This is a manifest file that'll be compiled into application.js, which will include all the files
// listed below.
//
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
// or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path.
//
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
// the compiled file.
//
// WARNING: THE FIRST BLANK LINE MARKS THE END OF WHAT'S TO BE PROCESSED, ANY BLANK LINE SHOULD
// GO AFTER THE REQUIRES BELOW.
//
/* eslint-disable spaced-comment */
//= require jquery
//= require jquery-ui
//= require jquery_ujs
//= require action_cable
//= require_directory ./lib
//= require ./cloudcarousel-secret
//= require ./metamaps.secret.bundle
//= require ./Metamaps.ServerData
//= require homepageVimeoFallback
/* eslint-enable spaced-comment */

View file

@ -1,22 +1,28 @@
// This is a manifest file that'll be compiled into application.js, which will include all the files
// listed below.
//
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
// or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path.
//
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
// the compiled file.
//
// WARNING: THE FIRST BLANK LINE MARKS THE END OF WHAT'S TO BE PROCESSED, ANY BLANK LINE SHOULD
// GO AFTER THE REQUIRES BELOW.
//
/* eslint-disable spaced-comment */
//= require jquery
//= require jquery-ui
//= require jquery_ujs
//= require action_cable
//= require_directory ./lib
//= require ./webpacked/metamaps.bundle
//= require ./Metamaps.ServerData
//= require homepageVimeoFallback
/* eslint-enable spaced-comment */
// This is a manifest file that'll be compiled into application.js, which will include all the files
// listed below.
//
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
// or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path.
//
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
// the compiled file.
//
// WARNING: THE FIRST BLANK LINE MARKS THE END OF WHAT'S TO BE PROCESSED, ANY BLANK LINE SHOULD
// GO AFTER THE REQUIRES BELOW.
//
//= require jquery
//= require jquery-ui
//= require jquery_ujs
//= require ./orderedLibraries/underscore
//= require ./orderedLibraries/backbone
//= require_directory ./lib
//= require ./src/Metamaps.GlobalUI
//= require ./src/Metamaps.Router
//= require ./src/Metamaps.Backbone
//= require ./src/Metamaps.Views
//= require ./src/JIT
//= require ./src/Metamaps
//= require ./src/Metamaps.JIT
//= require_directory ./shims
//= require_directory ./require
//= require_directory ./famous

View file

@ -1,438 +0,0 @@
//////////////////////////////////////////////////////////////////////////////////
// CloudCarousel V1.0.5
// (c) 2011 by R Cecco. <http://www.professorcloud.com>
// MIT License
//
// Reflection code based on plugin by Christophe Beyls <http://www.digitalia.be>
//
// Please retain this copyright header in all versions of the software
//////////////////////////////////////////////////////////////////////////////////
var matched, browser;
jQuery.uaMatch = function( ua ) {
ua = ua.toLowerCase();
var match = /(chrome)[ \/]([\w.]+)/.exec( ua ) ||
/(webkit)[ \/]([\w.]+)/.exec( ua ) ||
/(opera)(?:.*version|)[ \/]([\w.]+)/.exec( ua ) ||
/(msie) ([\w.]+)/.exec( ua ) ||
ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec( ua ) ||
[];
return {
browser: match[ 1 ] || "",
version: match[ 2 ] || "0"
};
};
matched = jQuery.uaMatch( navigator.userAgent );
browser = {};
if ( matched.browser ) {
browser[ matched.browser ] = true;
browser.version = matched.version;
}
// Chrome is Webkit, but Webkit is also Safari.
if ( browser.chrome ) {
browser.webkit = true;
} else if ( browser.webkit ) {
browser.safari = true;
}
jQuery.browser = browser;
(function($) {
// START Reflection object.
// Creates a reflection for underneath an image.
// IE uses an image with IE specific filter properties, other browsers use the Canvas tag.
// The position and size of the reflection gets updated by updateAll() in Controller.
function Reflection(img, reflHeight, opacity) {
var reflection, cntx, imageWidth = img.width, imageHeight = img.width, gradient, parent;
parent = $(img.parentNode);
this.element = reflection = parent.append("<canvas class='reflection' style='position:absolute'/>").find(':last')[0];
if ( !reflection.getContext && $.browser.msie) {
this.element = reflection = parent.append("<img class='reflection' style='position:absolute'/>").find(':last')[0];
reflection.src = img.src;
reflection.style.filter = "flipv progid:DXImageTransform.Microsoft.Alpha(opacity=" + (opacity * 100) + ", style=1, finishOpacity=0, startx=0, starty=0, finishx=0, finishy=" + (reflHeight / imageHeight * 100) + ")";
} else {
cntx = reflection.getContext("2d");
try {
$(reflection).attr({width: imageWidth, height: reflHeight});
cntx.save();
cntx.translate(0, imageHeight-1);
cntx.scale(1, -1);
cntx.drawImage(img, 0, 0, imageWidth, imageHeight);
cntx.restore();
cntx.globalCompositeOperation = "destination-out";
gradient = cntx.createLinearGradient(0, 0, 0, reflHeight);
gradient.addColorStop(0, "rgba(255, 255, 255, " + (1 - opacity) + ")");
gradient.addColorStop(1, "rgba(255, 255, 255, 1.0)");
cntx.fillStyle = gradient;
cntx.fillRect(0, 0, imageWidth, reflHeight);
} catch(e) {
return;
}
}
// Store a copy of the alt and title attrs into the reflection
$(reflection).attr({ 'alt': $(img).attr('alt'), title: $(img).attr('title')} );
} //END Reflection object
// START Item object.
// A wrapper object for items within the carousel.
var Item = function(imgIn, options)
{
this.orgWidth = imgIn.width;
this.orgHeight = imgIn.height;
this.image = imgIn;
this.reflection = null;
this.alt = imgIn.alt;
this.title = imgIn.title;
this.imageOK = false;
this.options = options;
this.imageOK = true;
if (this.options.reflHeight > 0)
{
this.reflection = new Reflection(this.image, this.options.reflHeight, this.options.reflOpacity);
}
$(this.image).css('position','absolute'); // Bizarre. This seems to reset image width to 0 on webkit!
};// END Item object
// Controller object.
// This handles moving all the items, dealing with mouse clicks etc.
var Controller = function(container, images, options)
{
var items = [], funcSin = Math.sin, funcCos = Math.cos, ctx=this;
this.controlTimer = 0;
this.stopped = false;
//this.imagesLoaded = 0;
this.container = container;
this.xRadius = options.xRadius;
this.yRadius = options.yRadius;
this.showFrontTextTimer = 0;
this.autoRotateTimer = 0;
if (options.xRadius === 0)
{
this.xRadius = ($(container).width()/2.3);
}
if (options.yRadius === 0)
{
this.yRadius = ($(container).height()/6);
}
this.xCentre = options.xPos;
this.yCentre = options.yPos;
this.frontIndex = 0; // Index of the item at the front
// Start with the first item at the front.
this.rotation = this.destRotation = Math.PI/2;
this.timeDelay = 1000/options.FPS;
// Turn on the infoBox
if(options.altBox !== null)
{
$(options.altBox).css('display','block');
$(options.titleBox).css('display','block');
}
// Turn on relative position for container to allow absolutely positioned elements
// within it to work.
$(container).css({ position:'relative', overflow:'hidden'} );
$(options.buttonLeft).css('display','inline');
$(options.buttonRight).css('display','inline');
// Setup the buttons.
$(options.buttonLeft).bind('mouseup',this,function(event){
event.data.rotate(-1);
return false;
});
$(options.buttonRight).bind('mouseup',this,function(event){
event.data.rotate(1);
return false;
});
// START METAMAPS CODE
// Add code that makes tab and shift+tab scroll through metacodes
$('.new_topic').bind('keydown',this,function(event){
if (event.keyCode == 9 && event.shiftKey) {
$(container).show()
event.data.rotate(-1);
event.preventDefault();
event.stopPropagation();
} else if (event.keyCode == 9) {
$(container).show()
event.data.rotate(1);
event.preventDefault();
event.stopPropagation();
}
});
// END METAMAPS CODE
// You will need this plugin for the mousewheel to work: http://plugins.jquery.com/project/mousewheel
if (options.mouseWheel)
{
// START METAMAPS CODE
/*$('body').bind('mousewheel',this,function(event, delta) {
if (Metamaps.Create.newTopic.beingCreated &&
!Metamaps.Create.isSwitchingSet &&
!Metamaps.Create.newTopic.pinned) {
event.data.rotate(delta);
return false;
}
});*/
// END METAMAPS CODE
// ORIGINAL CODE
// $(container).bind('mousewheel',this,function(event, delta) {
// event.data.rotate(delta);
// return false;
// });
//
}
$(container).unbind('mouseover click').bind('mouseover click',this,function(event){
clearInterval(event.data.autoRotateTimer); // Stop auto rotation if mouse over.
var text = $(event.target).attr('alt');
// If we have moved over a carousel item, then show the alt and title text.
if ( text !== undefined && text !== null )
{
clearTimeout(event.data.showFrontTextTimer);
$(options.altBox).html( ($(event.target).attr('alt') ));
//$(options.titleBox).html( ($(event.target).attr('title') ));
if ( options.bringToFront && event.type == 'click' )
{
$(options.titleBox).html( ($(event.target).attr('title') ));
// START METAMAPS CODE
Metamaps.Create.newTopic.metacode = $(event.target).attr('data-id');
// END METAMAPS CODE
var idx = $(event.target).data('itemIndex');
var frontIndex = event.data.frontIndex;
//var diff = idx - frontIndex;
var diff = (idx - frontIndex) % images.length;
if (Math.abs(diff) > images.length / 2) {
diff += (diff > 0 ? -images.length : images.length);
}
event.data.rotate(-diff);
}
}
});
// START METAMAPS CODE - initialize newTopic.metacode
var first = $(this.container).find('img').get(0)
Metamaps.Create.newTopic.metacode = $(first).data('id')
// END METAMAPS CODE
// If we have moved out of a carousel item (or the container itself),
// restore the text of the front item in 1 second.
$(container).bind('mouseout',this,function(event){
var context = event.data;
clearTimeout(context.showFrontTextTimer);
context.showFrontTextTimer = setTimeout( function(){context.showFrontText();},1000);
context.autoRotate(); // Start auto rotation.
});
// Prevent items from being selected as mouse is moved and clicked in the container.
$(container).bind('mousedown',this,function(event){
event.data.container.focus();
return false;
});
container.onselectstart = function () { return false; }; // For IE.
this.innerWrapper = $(container).wrapInner('<div style="position:absolute;width:100%;height:100%;"/>').children()[0];
// Shows the text from the front most item.
this.showFrontText = function()
{
if ( items[this.frontIndex] === undefined ) { return; } // Images might not have loaded yet.
// METAMAPS CODE
Metamaps.Create.newTopic.setMetacode($(items[this.frontIndex].image).attr('data-id'))
// NOT METAMAPS CODE
//$(options.titleBox).html( $(items[this.frontIndex].image).attr('title'));
//$(options.altBox).html( $(items[this.frontIndex].image).attr('alt'));
};
this.go = function()
{
if(this.controlTimer !== 0) { return; }
var context = this;
this.controlTimer = setTimeout( function(){context.updateAll();},this.timeDelay);
};
this.stop = function()
{
clearTimeout(this.controlTimer);
this.controlTimer = 0;
// METAMAPS CODE
$(container).hide()
// END METAMAPS CODE
};
// Starts the rotation of the carousel. Direction is the number (+-) of carousel items to rotate by.
this.rotate = function(direction)
{
this.frontIndex -= direction;
if (this.frontIndex == -1) this.frontIndex = items.length - 1;
this.frontIndex %= items.length;
this.destRotation += ( Math.PI / items.length ) * ( 2*direction );
this.showFrontText();
this.go();
};
this.autoRotate = function()
{
if ( options.autoRotate !== 'no' )
{
var dir = (options.autoRotate === 'right')? 1 : -1;
this.autoRotateTimer = setInterval( function(){ctx.rotate(dir); }, options.autoRotateDelay );
}
};
// This is the main loop function that moves everything.
this.updateAll = function()
{
var minScale = options.minScale; // This is the smallest scale applied to the furthest item.
var smallRange = (1-minScale) * 0.5;
var w,h,x,y,scale,item,sinVal;
var change = (this.destRotation - this.rotation);
var absChange = Math.abs(change);
this.rotation += change * options.speed;
if ( absChange < 0.001 ) { this.rotation = this.destRotation; }
var itemsLen = items.length;
var spacing = (Math.PI / itemsLen) * 2;
//var wrapStyle = null;
var radians = this.rotation;
var isMSIE = $.browser.msie;
// Turn off display. This can reduce repaints/reflows when making style and position changes in the loop.
// See http://dev.opera.com/articles/view/efficient-javascript/?page=3
this.innerWrapper.style.display = 'none';
var style;
var px = 'px', reflHeight;
var context = this;
for (var i = 0; i<itemsLen ;i++)
{
item = items[i];
sinVal = funcSin(radians);
scale = ((sinVal+1) * smallRange) + minScale;
x = this.xCentre + (( (funcCos(radians) * this.xRadius) - (item.orgWidth*0.5)) * scale);
y = this.yCentre + (( (sinVal * this.yRadius) ) * scale);
if (item.imageOK)
{
var img = item.image;
img.style.zIndex = "" + (scale * 100)>>0; // >>0 = Math.foor(). Firefox doesn't like fractional decimals in z-index.
w = img.width = item.orgWidth * scale;
h = img.height = item.orgHeight * scale;
img.style.left = x + px ;
img.style.top = y + px;
if (item.reflection !== null)
{
reflHeight = options.reflHeight * scale;
style = item.reflection.element.style;
style.left = x + px;
style.top = y + h + options.reflGap * scale + px;
style.width = w + px;
if (isMSIE)
{
style.filter.finishy = (reflHeight / h * 100);
}else
{
style.height = reflHeight + px;
}
}
}
radians += spacing;
}
// Turn display back on.
this.innerWrapper.style.display = 'block';
// If we have a preceptable change in rotation then loop again next frame.
if ( absChange >= 0.001 )
{
this.controlTimer = setTimeout( function(){context.updateAll();},this.timeDelay);
}else
{
// Otherwise just stop completely.
this.stop();
}
}; // END updateAll
// Create an Item object for each image
// func = function(){return;ctx.updateAll();} ;
// Check if images have loaded. We need valid widths and heights for the reflections.
this.checkImagesLoaded = function()
{
var i;
for(i=0;i<images.length;i++) {
if ( (images[i].width === undefined) || ( (images[i].complete !== undefined) && (!images[i].complete) ))
{
return;
}
}
for(i=0;i<images.length;i++) {
items.push( new Item( images[i], options ) );
$(images[i]).data('itemIndex',i);
}
// If all images have valid widths and heights, we can stop checking.
clearInterval(this.tt);
// METAMAPS COMMENT this.showFrontText();
this.autoRotate();
this.updateAll();
};
this.tt = setInterval( function(){ctx.checkImagesLoaded();},50);
}; // END Controller object
// The jQuery plugin part. Iterates through items specified in selector and inits a Controller class for each one.
$.fn.CloudCarousel = function(options) {
this.each( function() {
options = $.extend({}, {
reflHeight:0,
reflOpacity:0.5,
reflGap:0,
minScale:0.5,
xPos:0,
yPos:0,
xRadius:0,
yRadius:0,
altBox:null,
titleBox:null,
FPS: 30,
autoRotate: 'no',
autoRotateDelay: 1500,
speed:0.2,
mouseWheel: false,
bringToFront: false
},options );
// Create a Controller for each carousel.
$(this).data('cloudcarousel', new Controller( this, $('.cloudcarousel',$(this)), options) );
});
return this;
};
})(jQuery);

File diff suppressed because one or more lines are too long

View file

@ -1,29 +0,0 @@
/* global $ */
$(document).ready(function() {
if (window.location.pathname === '/') {
$.ajax({
type: 'GET',
url: 'https://i.vimeocdn.com/video/',
error: function(e) {
$('.homeVideo').hide()
$('.homeVideo').replaceWith($('<video/>', {
poster: '<%= asset_path('metamaps-intro-poster.webp') %>',
width: '560',
height: '315',
class: 'homeVideo',
controls: ''
}))
$('.homeVideo').append($('<source/>', {
src: 'https://metamaps.cc/videos/metamaps-intro.mp4',
type: 'video/mp4'
}))
$('.homeVideo').append(
'<p>You can watch our instruction video at ' +
'<a href="https://metamaps.cc/videos/metamaps-intro.mp4">' +
'https://metamaps.cc/videos/metamaps-intro.mp4</a>.'
)
}
})
}// if
})

Binary file not shown.

Binary file not shown.

View file

@ -1,161 +0,0 @@
// AjaxQ jQuery Plugin
// Copyright (c) 2012 Foliotek Inc.
// MIT License
// https://github.com/Foliotek/ajaxq
// Uses CommonJS, AMD or browser globals to create a jQuery plugin.
(function (factory) {
if (typeof define === 'function' && define.amd) {
// AMD. Register as an anonymous module.
define(['jquery'], factory);
} else if (typeof module === 'object' && module.exports) {
// Node/CommonJS
module.exports = factory(require('jquery'));
} else {
// Browser globals
factory(jQuery);
}
}(function ($) {
var queues = {};
var activeReqs = {};
// Register an $.ajaxq function, which follows the $.ajax interface, but allows a queue name which will force only one request per queue to fire.
// opts can be the regular $.ajax settings plainObject, or a callback returning the settings object, to be evaluated just prior to the actual call to $.ajax.
$.ajaxq = function(qname, opts) {
if (typeof opts === "undefined") {
throw ("AjaxQ: queue name is not provided");
}
// Will return a Deferred promise object extended with success/error/callback, so that this function matches the interface of $.ajax
var deferred = $.Deferred(),
promise = deferred.promise();
promise.success = promise.done;
promise.error = promise.fail;
promise.complete = promise.always;
// Check whether options are to be evaluated at call time or not.
var deferredOpts = typeof opts === 'function';
// Create a deep copy of the arguments, and enqueue this request.
var clonedOptions = !deferredOpts ? $.extend(true, {}, opts) : null;
enqueue(function() {
// Send off the ajax request now that the item has been removed from the queue
var jqXHR = $.ajax.apply(window, [deferredOpts ? opts() : clonedOptions]);
// Notify the returned deferred object with the correct context when the jqXHR is done or fails
// Note that 'always' will automatically be fired once one of these are called: http://api.jquery.com/category/deferred-object/.
jqXHR.done(function() {
deferred.resolve.apply(this, arguments);
});
jqXHR.fail(function() {
deferred.reject.apply(this, arguments);
});
jqXHR.always(dequeue); // make sure to dequeue the next request AFTER the done and fail callbacks are fired
return jqXHR;
});
return promise;
// If there is no queue, create an empty one and instantly process this item.
// Otherwise, just add this item onto it for later processing.
function enqueue(cb) {
if (!queues[qname]) {
queues[qname] = [];
var xhr = cb();
activeReqs[qname] = xhr;
}
else {
queues[qname].push(cb);
}
}
// Remove the next callback from the queue and fire it off.
// If the queue was empty (this was the last item), delete it from memory so the next one can be instantly processed.
function dequeue() {
if (!queues[qname]) {
return;
}
var nextCallback = queues[qname].shift();
if (nextCallback) {
var xhr = nextCallback();
activeReqs[qname] = xhr;
}
else {
delete queues[qname];
delete activeReqs[qname];
}
}
};
// Register a $.postq and $.getq method to provide shortcuts for $.get and $.post
// Copied from jQuery source to make sure the functions share the same defaults as $.get and $.post.
$.each( [ "getq", "postq" ], function( i, method ) {
$[ method ] = function( qname, url, data, callback, type ) {
if ( $.isFunction( data ) ) {
type = type || callback;
callback = data;
data = undefined;
}
return $.ajaxq(qname, {
type: method === "postq" ? "post" : "get",
url: url,
data: data,
success: callback,
dataType: type
});
};
});
var isQueueRunning = function(qname) {
return (queues.hasOwnProperty(qname) && queues[qname].length > 0) || activeReqs.hasOwnProperty(qname);
};
var isAnyQueueRunning = function() {
for (var i in queues) {
if (isQueueRunning(i)) return true;
}
return false;
};
$.ajaxq.isRunning = function(qname) {
if (qname) return isQueueRunning(qname);
else return isAnyQueueRunning();
};
$.ajaxq.getActiveRequest = function(qname) {
if (!qname) throw ("AjaxQ: queue name is required");
return activeReqs[qname];
};
$.ajaxq.abort = function(qname) {
if (!qname) throw ("AjaxQ: queue name is required");
var current = $.ajaxq.getActiveRequest(qname);
delete queues[qname];
delete activeReqs[qname];
if (current) current.abort();
};
$.ajaxq.clear = function(qname) {
if (!qname) {
for (var i in queues) {
if (queues.hasOwnProperty(i)) {
queues[i] = [];
}
}
}
else {
if (queues[qname]) {
queues[qname] = [];
}
}
};
}));

View file

@ -1,685 +0,0 @@
/*
* BestInPlace (for jQuery)
* version: 3.0.0.alpha (2014)
*
* By Bernat Farrero based on the work of Jan Varwig.
* Examples at http://bernatfarrero.com
*
* Licensed under the MIT:
* http://www.opensource.org/licenses/mit-license.php
*
* @requires jQuery
*
* Usage:
*
* Attention.
* The format of the JSON object given to the select inputs is the following:
* [["key", "value"],["key", "value"]]
* The format of the JSON object given to the checkbox inputs is the following:
* ["falseValue", "trueValue"]
*/
//= require jquery.autosize
function BestInPlaceEditor(e) {
'use strict';
this.element = e;
this.initOptions();
this.bindForm();
this.initPlaceHolder();
jQuery(this.activator).bind('click', {editor: this}, this.clickHandler);
}
BestInPlaceEditor.prototype = {
// Public Interface Functions //////////////////////////////////////////////
activate: function () {
'use strict';
var to_display;
if (this.isPlaceHolder()) {
to_display = "";
} else if (this.original_content) {
to_display = this.original_content;
} else {
switch (this.formType) {
case 'input':
case 'textarea':
if (this.display_raw) {
to_display = this.element.html().replace(/&amp;/gi, '&');
}
else {
var value = this.element.data('bipValue');
if (typeof value === 'undefined') {
to_display = '';
} else if (typeof value === 'string') {
to_display = this.element.data('bipValue').replace(/&amp;/gi, '&');
} else {
to_display = this.element.data('bipValue');
}
}
break;
case 'select':
to_display = this.element.html();
}
}
this.oldValue = this.isPlaceHolder() ? "" : this.element.html();
this.display_value = to_display;
jQuery(this.activator).unbind("click", this.clickHandler);
this.activateForm();
this.element.trigger(jQuery.Event("best_in_place:activate"));
},
abort: function () {
'use strict';
this.activateText(this.oldValue);
jQuery(this.activator).bind('click', {editor: this}, this.clickHandler);
this.element.trigger(jQuery.Event("best_in_place:abort"));
this.element.trigger(jQuery.Event("best_in_place:deactivate"));
},
abortIfConfirm: function () {
'use strict';
if (!this.useConfirm) {
this.abort();
return;
}
if (confirm(BestInPlaceEditor.defaults.locales[''].confirmMessage)) {
this.abort();
}
},
update: function () {
'use strict';
var editor = this,
value = this.getValue();
// Avoid request if no change is made
if (this.formType in {"input": 1, "textarea": 1} && value === this.oldValue) {
this.abort();
return true;
}
editor.ajax({
"type": this.requestMethod(),
"dataType": BestInPlaceEditor.defaults.ajaxDataType,
"data": editor.requestData(),
"success": function (data, status, xhr) {
editor.loadSuccessCallback(data, status, xhr);
},
"error": function (request, error) {
editor.loadErrorCallback(request, error);
}
});
switch (this.formType) {
case "select":
this.previousCollectionValue = value;
// search for the text for the span
$.each(this.values, function(index, arr){ if (String(arr[0]) === String(value)) editor.element.html(arr[1]); });
break;
case "checkbox":
$.each(this.values, function(index, arr){ if (String(arr[0]) === String(value)) editor.element.html(arr[1]); });
break;
default:
if (value !== "") {
if (this.display_raw) {
editor.element.html(value);
} else {
editor.element.text(value);
}
} else {
editor.element.html(this.placeHolder);
}
}
editor.element.data('bipValue', value);
editor.element.attr('data-bip-value', value);
editor.element.trigger(jQuery.Event("best_in_place:update"));
},
activateForm: function () {
'use strict';
alert(BestInPlaceEditor.defaults.locales[''].uninitializedForm);
},
activateText: function (value) {
'use strict';
this.element.html(value);
if (this.isPlaceHolder()) {
this.element.html(this.placeHolder);
}
},
// Helper Functions ////////////////////////////////////////////////////////
initOptions: function () {
// Try parent supplied info
'use strict';
var self = this;
self.element.parents().each(function () {
var $parent = jQuery(this);
self.url = self.url || $parent.data("bipUrl");
self.activator = self.activator || $parent.data("bipActivator");
self.okButton = self.okButton || $parent.data("bipOkButton");
self.okButtonClass = self.okButtonClass || $parent.data("bipOkButtonClass");
self.cancelButton = self.cancelButton || $parent.data("bipCancelButton");
self.cancelButtonClass = self.cancelButtonClass || $parent.data("bipCancelButtonClass");
self.skipBlur = self.skipBlur || $parent.data("bipSkipBlur");
});
// Load own attributes (overrides all others)
self.url = self.element.data("bipUrl") || self.url || document.location.pathname;
self.collection = self.element.data("bipCollection") || self.collection;
self.formType = self.element.data("bipType") || "input";
self.objectName = self.element.data("bipObject") || self.objectName;
self.attributeName = self.element.data("bipAttribute") || self.attributeName;
self.activator = self.element.data("bipActivator") || self.element;
self.okButton = self.element.data("bipOkButton") || self.okButton;
self.okButtonClass = self.element.data("bipOkButtonClass") || self.okButtonClass || BestInPlaceEditor.defaults.okButtonClass;
self.cancelButton = self.element.data("bipCancelButton") || self.cancelButton;
self.cancelButtonClass = self.element.data("bipCancelButtonClass") || self.cancelButtonClass || BestInPlaceEditor.defaults.cancelButtonClass;
self.skipBlur = self.element.data("bipSkipBlur") || self.skipBlur || BestInPlaceEditor.defaults.skipBlur;
self.isNewObject = self.element.data("bipNewObject");
self.dataExtraPayload = self.element.data("bipExtraPayload");
// Fix for default values of 0
if (self.element.data("bipPlaceholder") == null) {
self.placeHolder = BestInPlaceEditor.defaults.locales[''].placeHolder;
} else {
self.placeHolder = self.element.data("bipPlaceholder");
}
self.inner_class = self.element.data("bipInnerClass");
self.html_attrs = self.element.data("bipHtmlAttrs");
self.original_content = self.element.data("bipOriginalContent") || self.original_content;
// if set the input won't be satinized
self.display_raw = self.element.data("bip-raw");
self.useConfirm = self.element.data("bip-confirm");
if (self.formType === "select" || self.formType === "checkbox") {
self.values = self.collection;
self.collectionValue = self.element.data("bipValue") || self.collectionValue;
}
},
bindForm: function () {
'use strict';
this.activateForm = BestInPlaceEditor.forms[this.formType].activateForm;
this.getValue = BestInPlaceEditor.forms[this.formType].getValue;
},
initPlaceHolder: function () {
'use strict';
// TODO add placeholder for select and checkbox
if (this.element.html() === "") {
this.element.addClass('bip-placeholder');
this.element.html(this.placeHolder);
}
},
isPlaceHolder: function () {
'use strict';
// TODO: It only work when form is deactivated.
// Condition will fail when form is activated
return this.element.html() === "" || this.element.html() === this.placeHolder;
},
getValue: function () {
'use strict';
alert(BestInPlaceEditor.defaults.locales[''].uninitializedForm);
},
// Trim and Strips HTML from text
sanitizeValue: function (s) {
'use strict';
return jQuery.trim(s);
},
requestMethod: function() {
'use strict';
return this.isNewObject ? 'post' : BestInPlaceEditor.defaults.ajaxMethod;
},
/* Generate the data sent in the POST request */
requestData: function () {
'use strict';
// To prevent xss attacks, a csrf token must be defined as a meta attribute
var csrf_token = jQuery('meta[name=csrf-token]').attr('content'),
csrf_param = jQuery('meta[name=csrf-param]').attr('content');
var data = {}
data['_method'] = this.requestMethod()
data[this.objectName] = this.dataExtraPayload || {}
data[this.objectName][this.attributeName] = this.getValue()
if (csrf_param !== undefined && csrf_token !== undefined) {
data[csrf_param] = csrf_token
}
return jQuery.param(data);
},
ajax: function (options) {
'use strict';
options.url = this.url;
options.beforeSend = function (xhr) {
xhr.setRequestHeader("Accept", "application/json");
};
return jQuery.ajax(options);
},
// Handlers ////////////////////////////////////////////////////////////////
loadSuccessCallback: function (data, status, xhr) {
'use strict';
data = jQuery.trim(data);
//Update original content with current text.
if (this.display_raw) {
this.original_content = this.element.html();
} else {
this.original_content = this.element.text();
}
if (data && data !== "") {
var response = jQuery.parseJSON(data);
if (response !== null && response.hasOwnProperty("display_as")) {
this.element.data('bip-original-content', this.element.text());
this.element.html(response.display_as);
}
if (this.isNewObject && response && response[this.objectName]) {
if (response[this.objectName]["id"]) {
this.isNewObject = false
this.url += "/" + response[this.objectName]["id"] // in REST a POST /thing url should become PUT /thing/123
}
}
}
this.element.toggleClass('bip-placeholder', this.isPlaceHolder());
this.element.trigger(jQuery.Event("best_in_place:success"), [data, status, xhr]);
this.element.trigger(jQuery.Event("ajax:success"), [data, status, xhr]);
// Binding back after being clicked
jQuery(this.activator).bind('click', {editor: this}, this.clickHandler);
this.element.trigger(jQuery.Event("best_in_place:deactivate"));
if (this.collectionValue !== null && this.formType === "select") {
this.collectionValue = this.previousCollectionValue;
this.previousCollectionValue = null;
}
},
loadErrorCallback: function (request, error) {
'use strict';
this.activateText(this.oldValue);
this.element.trigger(jQuery.Event("best_in_place:error"), [request, error]);
this.element.trigger(jQuery.Event("ajax:error"), request, error);
// Binding back after being clicked
jQuery(this.activator).bind('click', {editor: this}, this.clickHandler);
this.element.trigger(jQuery.Event("best_in_place:deactivate"));
},
clickHandler: function (event) {
'use strict';
event.preventDefault();
event.data.editor.activate();
},
setHtmlAttributes: function () {
'use strict';
var formField = this.element.find(this.formType);
if (this.html_attrs) {
var attrs = this.html_attrs;
$.each(attrs, function (key, val) {
formField.attr(key, val);
});
}
},
placeButtons: function (output, field) {
'use strict';
if (field.okButton) {
output.append(
jQuery(document.createElement('input'))
.attr('type', 'submit')
.attr('class', field.okButtonClass)
.attr('value', field.okButton)
);
}
if (field.cancelButton) {
output.append(
jQuery(document.createElement('input'))
.attr('type', 'button')
.attr('class', field.cancelButtonClass)
.attr('value', field.cancelButton)
);
}
}
};
// Button cases:
// If no buttons, then blur saves, ESC cancels
// If just Cancel button, then blur saves, ESC or clicking Cancel cancels (careful of blur event!)
// If just OK button, then clicking OK saves (careful of blur event!), ESC or blur cancels
// If both buttons, then clicking OK saves, ESC or clicking Cancel or blur cancels
BestInPlaceEditor.forms = {
"input": {
activateForm: function () {
'use strict';
var output = jQuery(document.createElement('form'))
.addClass('form_in_place')
.attr('action', 'javascript:void(0);')
.attr('style', 'display:inline');
var input_elt = jQuery(document.createElement('input'))
.attr('type', 'text')
.attr('name', this.attributeName)
.val(this.display_value);
// Add class to form input
if (this.inner_class) {
input_elt.addClass(this.inner_class);
}
output.append(input_elt);
this.placeButtons(output, this);
this.element.html(output);
this.setHtmlAttributes();
this.element.find("input[type='text']")[0].select();
this.element.find("form").bind('submit', {editor: this}, BestInPlaceEditor.forms.input.submitHandler);
if (this.cancelButton) {
this.element.find("input[type='button']").bind('click', {editor: this}, BestInPlaceEditor.forms.input.cancelButtonHandler);
}
if (!this.okButton) {
this.element.find("input[type='text']").bind('blur', {editor: this}, BestInPlaceEditor.forms.input.inputBlurHandler);
}
this.element.find("input[type='text']").bind('keyup', {editor: this}, BestInPlaceEditor.forms.input.keyupHandler);
this.blurTimer = null;
this.userClicked = false;
},
getValue: function () {
'use strict';
return this.sanitizeValue(this.element.find("input").val());
},
// When buttons are present, use a timer on the blur event to give precedence to clicks
inputBlurHandler: function (event) {
'use strict';
if (event.data.editor.okButton) {
event.data.editor.blurTimer = setTimeout(function () {
if (!event.data.editor.userClicked) {
event.data.editor.abort();
}
}, 500);
} else {
if (event.data.editor.cancelButton) {
event.data.editor.blurTimer = setTimeout(function () {
if (!event.data.editor.userClicked) {
event.data.editor.update();
}
}, 500);
} else {
event.data.editor.update();
}
}
},
submitHandler: function (event) {
'use strict';
event.data.editor.userClicked = true;
clearTimeout(event.data.editor.blurTimer);
event.data.editor.update();
},
cancelButtonHandler: function (event) {
'use strict';
event.data.editor.userClicked = true;
clearTimeout(event.data.editor.blurTimer);
event.data.editor.abort();
event.stopPropagation(); // Without this, click isn't handled
},
keyupHandler: function (event) {
'use strict';
if (event.keyCode === 27) {
event.data.editor.abort();
event.stopImmediatePropagation();
}
}
},
"select": {
activateForm: function () {
'use strict';
var output = jQuery(document.createElement('form'))
.attr('action', 'javascript:void(0)')
.attr('style', 'display:inline'),
selected = '',
select_elt = jQuery(document.createElement('select'))
.attr('class', this.inner_class !== null ? this.inner_class : ''),
currentCollectionValue = this.collectionValue,
key, value,
a = this.values;
$.each(a, function(index, arr){
key = arr[0];
value = arr[1];
var option_elt = jQuery(document.createElement('option'))
.val(key)
.html(value);
if (currentCollectionValue) {
if (String(key) === String(currentCollectionValue)) option_elt.attr('selected', 'selected');
}
select_elt.append(option_elt);
});
output.append(select_elt);
this.element.html(output);
this.setHtmlAttributes();
this.element.find("select").bind('change', {editor: this}, BestInPlaceEditor.forms.select.blurHandler);
this.element.find("select").bind('blur', {editor: this}, BestInPlaceEditor.forms.select.blurHandler);
this.element.find("select").bind('keyup', {editor: this}, BestInPlaceEditor.forms.select.keyupHandler);
this.element.find("select")[0].focus();
// automatically click on the select so you
// don't have to click twice
try {
var e = document.createEvent("MouseEvents");
e.initMouseEvent("mousedown", true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
this.element.find("select")[0].dispatchEvent(e);
}
catch(e) {
// browser doesn't support this, e.g. IE8
}
},
getValue: function () {
'use strict';
return this.sanitizeValue(this.element.find("select").val());
},
blurHandler: function (event) {
'use strict';
event.data.editor.update();
},
keyupHandler: function (event) {
'use strict';
if (event.keyCode === 27) {
event.data.editor.abort();
}
}
},
"checkbox": {
activateForm: function () {
'use strict';
this.collectionValue = !this.getValue();
this.setHtmlAttributes();
this.update();
},
getValue: function () {
'use strict';
return this.collectionValue;
}
},
"textarea": {
activateForm: function () {
'use strict';
// grab width and height of text
var width = this.element.css('width');
var height = this.element.css('height');
// construct form
var output = jQuery(document.createElement('form'))
.addClass('form_in_place')
.attr('action', 'javascript:void(0);')
.attr('style', 'display:inline');
var textarea_elt = jQuery(document.createElement('textarea'))
.attr('name', this.attributeName)
.val(this.sanitizeValue(this.display_value));
if (this.inner_class !== null) {
textarea_elt.addClass(this.inner_class);
}
output.append(textarea_elt);
this.placeButtons(output, this);
this.element.html(output);
this.setHtmlAttributes();
// set width and height of textarea
jQuery(this.element.find("textarea")[0]).css({'min-width': width, 'min-height': height});
jQuery(this.element.find("textarea")[0]).autosize();
this.element.find("textarea")[0].focus();
this.element.find("form").bind('submit', {editor: this}, BestInPlaceEditor.forms.textarea.submitHandler);
if (this.cancelButton) {
this.element.find("input[type='button']").bind('click', {editor: this}, BestInPlaceEditor.forms.textarea.cancelButtonHandler);
}
if (!this.skipBlur) {
this.element.find("textarea").bind('blur', {editor: this}, BestInPlaceEditor.forms.textarea.blurHandler);
}
this.element.find("textarea").bind('keyup', {editor: this}, BestInPlaceEditor.forms.textarea.keyupHandler);
this.blurTimer = null;
this.userClicked = false;
},
getValue: function () {
'use strict';
return this.sanitizeValue(this.element.find("textarea").val());
},
// When buttons are present, use a timer on the blur event to give precedence to clicks
blurHandler: function (event) {
'use strict';
if (event.data.editor.okButton) {
event.data.editor.blurTimer = setTimeout(function () {
if (!event.data.editor.userClicked) {
event.data.editor.abortIfConfirm();
}
}, 500);
} else {
if (event.data.editor.cancelButton) {
event.data.editor.blurTimer = setTimeout(function () {
if (!event.data.editor.userClicked) {
event.data.editor.update();
}
}, 500);
} else {
event.data.editor.update();
}
}
},
submitHandler: function (event) {
'use strict';
event.data.editor.userClicked = true;
clearTimeout(event.data.editor.blurTimer);
event.data.editor.update();
},
cancelButtonHandler: function (event) {
'use strict';
event.data.editor.userClicked = true;
clearTimeout(event.data.editor.blurTimer);
event.data.editor.abortIfConfirm();
event.stopPropagation(); // Without this, click isn't handled
},
keyupHandler: function (event) {
'use strict';
if (event.keyCode === 27) {
event.data.editor.abortIfConfirm();
}
}
}
};
BestInPlaceEditor.defaults = {
locales: {},
ajaxMethod: "put", //TODO Change to patch when support to 3.2 is dropped
ajaxDataType: 'text',
okButtonClass: '',
cancelButtonClass: '',
skipBlur: false
};
// Default locale
BestInPlaceEditor.defaults.locales[''] = {
confirmMessage: "Are you sure you want to discard your changes?",
uninitializedForm: "The form was not properly initialized. getValue is unbound",
placeHolder: '-'
};
jQuery.fn.best_in_place = function () {
'use strict';
function setBestInPlace(element) {
if (!element.data('bestInPlaceEditor')) {
element.data('bestInPlaceEditor', new BestInPlaceEditor(element));
return true;
}
}
jQuery(this.context).delegate(this.selector, 'click', function () {
var el = jQuery(this);
if (setBestInPlace(el)) {
el.click();
}
});
this.each(function () {
setBestInPlace(jQuery(this));
});
return this;
};

View file

@ -0,0 +1,780 @@
/*
BestInPlace (for jQuery)
version: 0.1.0 (01/01/2011)
@requires jQuery >= v1.4
@requires jQuery.purr to display pop-up windows
By Bernat Farrero based on the work of Jan Varwig.
Examples at http://bernatfarrero.com
Licensed under the MIT:
http://www.opensource.org/licenses/mit-license.php
Usage:
Attention.
The format of the JSON object given to the select inputs is the following:
[["key", "value"],["key", "value"]]
The format of the JSON object given to the checkbox inputs is the following:
["falseValue", "trueValue"]
*/
function BestInPlaceEditor(e) {
this.element = e;
this.initOptions();
this.bindForm();
this.initNil();
jQuery(this.activator).bind('click', {editor: this}, this.clickHandler);
}
BestInPlaceEditor.prototype = {
// Public Interface Functions //////////////////////////////////////////////
activate : function() {
var to_display = "";
if (this.isNil()) {
to_display = "";
}
else if (this.original_content) {
to_display = this.original_content;
}
else {
if (this.sanitize) {
to_display = this.element.text();
} else {
to_display = this.element.html();
}
}
this.oldValue = this.isNil() ? "" : this.element.html();
this.display_value = to_display;
jQuery(this.activator).unbind("click", this.clickHandler);
this.activateForm();
this.element.trigger(jQuery.Event("best_in_place:activate"));
},
abort : function() {
this.activateText(this.oldValue);
jQuery(this.activator).bind('click', {editor: this}, this.clickHandler);
this.element.trigger(jQuery.Event("best_in_place:abort"));
this.element.trigger(jQuery.Event("best_in_place:deactivate"));
},
abortIfConfirm : function () {
if (!this.useConfirm) {
this.abort();
return;
}
if (confirm("Are you sure you want to discard your changes?")) {
this.abort();
}
},
update : function() {
var editor = this;
if (this.formType in {"input":1, "textarea":1} && this.getValue() == this.oldValue)
{ // Avoid request if no change is made
this.abort();
return true;
}
editor.ajax({
"type" : "post",
"dataType" : "text",
"data" : editor.requestData(),
"success" : function(data){ editor.loadSuccessCallback(data); },
"error" : function(request, error){ editor.loadErrorCallback(request, error); }
});
if (this.formType == "select") {
var value = this.getValue();
this.previousCollectionValue = value;
jQuery.each(this.values, function(i, v) {
if (value == v[0]) {
editor.element.html(v[1]);
}
}
);
} else if (this.formType == "checkbox") {
editor.element.html(this.getValue() ? this.values[1] : this.values[0]);
} else {
if (this.getValue() !== "") {
editor.element.text(this.getValue());
} else {
editor.element.html(this.nil);
}
}
editor.element.trigger(jQuery.Event("best_in_place:update"));
},
activateForm : function() {
alert("The form was not properly initialized. activateForm is unbound");
},
activateText : function(value){
this.element.html(value);
if(this.isNil()) this.element.html(this.nil);
},
// Helper Functions ////////////////////////////////////////////////////////
initOptions : function() {
// Try parent supplied info
var self = this;
self.element.parents().each(function(){
$parent = jQuery(this);
self.url = self.url || $parent.attr("data-url");
self.collection = self.collection || $parent.attr("data-collection");
self.formType = self.formType || $parent.attr("data-type");
self.objectName = self.objectName || $parent.attr("data-object");
self.attributeName = self.attributeName || $parent.attr("data-attribute");
self.activator = self.activator || $parent.attr("data-activator");
self.okButton = self.okButton || $parent.attr("data-ok-button");
self.okButtonClass = self.okButtonClass || $parent.attr("data-ok-button-class");
self.cancelButton = self.cancelButton || $parent.attr("data-cancel-button");
self.cancelButtonClass = self.cancelButtonClass || $parent.attr("data-cancel-button-class");
self.nil = self.nil || $parent.attr("data-nil");
self.inner_class = self.inner_class || $parent.attr("data-inner-class");
self.html_attrs = self.html_attrs || $parent.attr("data-html-attrs");
self.original_content = self.original_content || $parent.attr("data-original-content");
self.collectionValue = self.collectionValue || $parent.attr("data-value");
});
// Try Rails-id based if parents did not explicitly supply something
self.element.parents().each(function(){
var res = this.id.match(/^(\w+)_(\d+)$/i);
if (res) {
self.objectName = self.objectName || res[1];
}
});
// Load own attributes (overrides all others)
self.url = self.element.attr("data-url") || self.url || document.location.pathname;
self.collection = self.element.attr("data-collection") || self.collection;
self.formType = self.element.attr("data-type") || self.formtype || "input";
self.objectName = self.element.attr("data-object") || self.objectName;
self.attributeName = self.element.attr("data-attribute") || self.attributeName;
self.activator = self.element.attr("data-activator") || self.element;
self.okButton = self.element.attr("data-ok-button") || self.okButton;
self.okButtonClass = self.element.attr("data-ok-button-class") || self.okButtonClass || "";
self.cancelButton = self.element.attr("data-cancel-button") || self.cancelButton;
self.cancelButtonClass = self.element.attr("data-cancel-button-class") || self.cancelButtonClass || "";
self.nil = self.element.attr("data-nil") || self.nil || "—";
self.inner_class = self.element.attr("data-inner-class") || self.inner_class || null;
self.html_attrs = self.element.attr("data-html-attrs") || self.html_attrs;
self.original_content = self.element.attr("data-original-content") || self.original_content;
self.collectionValue = self.element.attr("data-value") || self.collectionValue;
if (!self.element.attr("data-sanitize")) {
self.sanitize = true;
}
else {
self.sanitize = (self.element.attr("data-sanitize") == "true");
}
if (!self.element.attr("data-use-confirm")) {
self.useConfirm = true;
} else {
self.useConfirm = (self.element.attr("data-use-confirm") != "false");
}
if ((self.formType == "select" || self.formType == "checkbox") && self.collection !== null)
{
self.values = jQuery.parseJSON(self.collection);
}
},
bindForm : function() {
this.activateForm = BestInPlaceEditor.forms[this.formType].activateForm;
this.getValue = BestInPlaceEditor.forms[this.formType].getValue;
},
initNil: function() {
if (this.element.html() === "")
{
this.element.html(this.nil);
}
},
isNil: function() {
// TODO: It only work when form is deactivated.
// Condition will fail when form is activated
return this.element.html() === "" || this.element.html() === this.nil;
},
getValue : function() {
alert("The form was not properly initialized. getValue is unbound");
},
// Trim and Strips HTML from text
sanitizeValue : function(s) {
return jQuery.trim(s);
},
/* Generate the data sent in the POST request */
requestData : function() {
// To prevent xss attacks, a csrf token must be defined as a meta attribute
csrf_token = jQuery('meta[name=csrf-token]').attr('content');
csrf_param = jQuery('meta[name=csrf-param]').attr('content');
var data = "_method=put";
data += "&" + this.objectName + '[' + this.attributeName + ']=' + encodeURIComponent(this.getValue());
if (csrf_param !== undefined && csrf_token !== undefined) {
data += "&" + csrf_param + "=" + encodeURIComponent(csrf_token);
}
return data;
},
ajax : function(options) {
options.url = this.url;
options.beforeSend = function(xhr){ xhr.setRequestHeader("Accept", "application/json"); };
return jQuery.ajax(options);
},
// Handlers ////////////////////////////////////////////////////////////////
loadSuccessCallback : function(data) {
data = jQuery.trim(data);
if(data && data!=""){
var response = jQuery.parseJSON(jQuery.trim(data));
if (response !== null && response.hasOwnProperty("display_as")) {
this.element.attr("data-original-content", this.element.text());
this.original_content = this.element.text();
this.element.html(response["display_as"]);
}
this.element.trigger(jQuery.Event("best_in_place:success"), data);
this.element.trigger(jQuery.Event("ajax:success"), data);
} else {
this.element.trigger(jQuery.Event("best_in_place:success"));
this.element.trigger(jQuery.Event("ajax:success"));
}
// Binding back after being clicked
jQuery(this.activator).bind('click', {editor: this}, this.clickHandler);
this.element.trigger(jQuery.Event("best_in_place:deactivate"));
if (this.collectionValue !== null && this.formType == "select") {
this.collectionValue = this.previousCollectionValue;
this.previousCollectionValue = null;
}
},
loadErrorCallback : function(request, error) {
this.activateText(this.oldValue);
this.element.trigger(jQuery.Event("best_in_place:error"), [request, error]);
this.element.trigger(jQuery.Event("ajax:error"), request, error);
// Binding back after being clicked
jQuery(this.activator).bind('click', {editor: this}, this.clickHandler);
this.element.trigger(jQuery.Event("best_in_place:deactivate"));
},
clickHandler : function(event) {
event.preventDefault();
event.data.editor.activate();
},
setHtmlAttributes : function() {
var formField = this.element.find(this.formType);
if(this.html_attrs){
var attrs = jQuery.parseJSON(this.html_attrs);
for(var key in attrs){
formField.attr(key, attrs[key]);
}
}
}
};
// Button cases:
// If no buttons, then blur saves, ESC cancels
// If just Cancel button, then blur saves, ESC or clicking Cancel cancels (careful of blur event!)
// If just OK button, then clicking OK saves (careful of blur event!), ESC or blur cancels
// If both buttons, then clicking OK saves, ESC or clicking Cancel or blur cancels
BestInPlaceEditor.forms = {
"input" : {
activateForm : function() {
var output = jQuery(document.createElement('form'))
.addClass('form_in_place')
.attr('action', 'javascript:void(0);')
.attr('style', 'display:inline');
var input_elt = jQuery(document.createElement('input'))
.attr('type', 'text')
.attr('name', this.attributeName)
.val(this.display_value);
if(this.inner_class !== null) {
input_elt.addClass(this.inner_class);
}
output.append(input_elt);
if(this.okButton) {
output.append(
jQuery(document.createElement('input'))
.attr('type', 'submit')
.attr('class', this.okButtonClass)
.attr('value', this.okButton)
)
}
if(this.cancelButton) {
output.append(
jQuery(document.createElement('input'))
.attr('type', 'button')
.attr('class', this.cancelButtonClass)
.attr('value', this.cancelButton)
)
}
this.element.html(output);
this.setHtmlAttributes();
// START METAMAPS CODE
//this.element.find("input[type='text']")[0].select();
this.element.find("input[type='text']")[0].focus();
// END METAMAPS CODE
this.element.find("form").bind('submit', {editor: this}, BestInPlaceEditor.forms.input.submitHandler);
if (this.cancelButton) {
this.element.find("input[type='button']").bind('click', {editor: this}, BestInPlaceEditor.forms.input.cancelButtonHandler);
}
this.element.find("input[type='text']").bind('blur', {editor: this}, BestInPlaceEditor.forms.input.inputBlurHandler);
// START METAMAPS CODE
this.element.find("input[type='text']").bind('keydown', {editor: this}, BestInPlaceEditor.forms.input.keydownHandler);
// END METAMAPS CODE
this.element.find("input[type='text']").bind('keyup', {editor: this}, BestInPlaceEditor.forms.input.keyupHandler);
this.blurTimer = null;
this.userClicked = false;
},
getValue : function() {
return this.sanitizeValue(this.element.find("input").val());
},
// When buttons are present, use a timer on the blur event to give precedence to clicks
inputBlurHandler : function(event) {
if (event.data.editor.okButton) {
event.data.editor.blurTimer = setTimeout(function () {
if (!event.data.editor.userClicked) {
event.data.editor.abort();
}
}, 500);
} else {
if (event.data.editor.cancelButton) {
event.data.editor.blurTimer = setTimeout(function () {
if (!event.data.editor.userClicked) {
event.data.editor.update();
}
}, 500);
} else {
event.data.editor.update();
}
}
},
submitHandler : function(event) {
event.data.editor.userClicked = true;
clearTimeout(event.data.editor.blurTimer);
event.data.editor.update();
},
cancelButtonHandler : function(event) {
event.data.editor.userClicked = true;
clearTimeout(event.data.editor.blurTimer);
event.data.editor.abort();
event.stopPropagation(); // Without this, click isn't handled
},
keyupHandler : function(event) {
if (event.keyCode == 27) {
event.data.editor.abort();
}
// START METAMAPS CODE
else if (event.keyCode == 13 && !event.shiftKey) {
event.data.editor.update();
}
// END METAMAPS CODE
}
},
"date" : {
activateForm : function() {
var that = this,
output = jQuery(document.createElement('form'))
.addClass('form_in_place')
.attr('action', 'javascript:void(0);')
.attr('style', 'display:inline'),
input_elt = jQuery(document.createElement('input'))
.attr('type', 'text')
.attr('name', this.attributeName)
.attr('value', this.sanitizeValue(this.display_value));
if(this.inner_class !== null) {
input_elt.addClass(this.inner_class);
}
output.append(input_elt)
this.element.html(output);
this.setHtmlAttributes();
this.element.find('input')[0].select();
this.element.find("form").bind('submit', {editor: this}, BestInPlaceEditor.forms.input.submitHandler);
this.element.find("input").bind('keyup', {editor: this}, BestInPlaceEditor.forms.input.keyupHandler);
this.element.find('input')
.datepicker({
onClose: function() {
that.update();
}
})
.datepicker('show');
},
getValue : function() {
return this.sanitizeValue(this.element.find("input").val());
},
submitHandler : function(event) {
event.data.editor.update();
},
// START METAMAPS CODE
keydownHandler : function(event) {
if (event.keyCode == 13 && !event.shiftKey) {
event.preventDefault();
event.stopPropagation();
return false;
}
},
// END METAMAPS CODE
keyupHandler : function(event) {
if (event.keyCode == 27) {
event.data.editor.abort();
}
}
},
"select" : {
activateForm : function() {
var output = jQuery(document.createElement('form'))
.attr('action', 'javascript:void(0)')
.attr('style', 'display:inline');
selected = '',
oldValue = this.oldValue,
select_elt = jQuery(document.createElement('select'))
.attr('class', this.inned_class !== null ? this.inner_class : '' ),
currentCollectionValue = this.collectionValue;
jQuery.each(this.values, function (index, value) {
var option_elt = jQuery(document.createElement('option'))
// .attr('value', value[0])
.val(value[0])
.html(value[1]);
if(value[0] == currentCollectionValue) {
option_elt.attr('selected', 'selected');
}
select_elt.append(option_elt);
});
output.append(select_elt);
this.element.html(output);
this.setHtmlAttributes();
this.element.find("select").bind('change', {editor: this}, BestInPlaceEditor.forms.select.blurHandler);
this.element.find("select").bind('blur', {editor: this}, BestInPlaceEditor.forms.select.blurHandler);
this.element.find("select").bind('keyup', {editor: this}, BestInPlaceEditor.forms.select.keyupHandler);
this.element.find("select")[0].focus();
},
getValue : function() {
return this.sanitizeValue(this.element.find("select").val());
// return this.element.find("select").val();
},
blurHandler : function(event) {
event.data.editor.update();
},
keyupHandler : function(event) {
if (event.keyCode == 27) event.data.editor.abort();
}
},
"checkbox" : {
activateForm : function() {
this.collectionValue = !this.getValue();
this.setHtmlAttributes();
this.update();
},
getValue : function() {
return this.collectionValue;
}
},
"textarea" : {
activateForm : function() {
// grab width and height of text
width = this.element.css('width');
height = this.element.css('height');
// construct form
var output = jQuery(document.createElement('form'))
.attr('action', 'javascript:void(0)')
.attr('style', 'display:inline')
.append(jQuery(document.createElement('textarea'))
.val(this.sanitizeValue(this.display_value)));
if(this.okButton) {
output.append(
jQuery(document.createElement('input'))
.attr('type', 'submit')
.attr('value', this.okButton)
);
}
if(this.cancelButton) {
output.append(
jQuery(document.createElement('input'))
.attr('type', 'button')
.attr('value', this.cancelButton)
)
}
this.element.html(output);
this.setHtmlAttributes();
// set width and height of textarea
jQuery(this.element.find("textarea")[0]).css({ 'min-width': width, 'min-height': height });
jQuery(this.element.find("textarea")[0]).elastic();
this.element.find("textarea")[0].focus();
this.element.find("form").bind('submit', {editor: this}, BestInPlaceEditor.forms.textarea.submitHandler);
if (this.cancelButton) {
this.element.find("input[type='button']").bind('click', {editor: this}, BestInPlaceEditor.forms.textarea.cancelButtonHandler);
}
this.element.find("textarea").bind('blur', {editor: this}, BestInPlaceEditor.forms.textarea.blurHandler);
// START METAMAPS CODE
this.element.find("textarea").bind('keydown', {editor: this}, BestInPlaceEditor.forms.textarea.keydownHandler);
// END METAMAPS CODE
this.element.find("textarea").bind('keyup', {editor: this}, BestInPlaceEditor.forms.textarea.keyupHandler);
this.blurTimer = null;
this.userClicked = false;
},
getValue : function() {
return this.sanitizeValue(this.element.find("textarea").val());
},
// When buttons are present, use a timer on the blur event to give precedence to clicks
blurHandler : function(event) {
if (event.data.editor.okButton) {
event.data.editor.blurTimer = setTimeout(function () {
if (!event.data.editor.userClicked) {
event.data.editor.abortIfConfirm();
}
}, 500);
} else {
if (event.data.editor.cancelButton) {
event.data.editor.blurTimer = setTimeout(function () {
if (!event.data.editor.userClicked) {
event.data.editor.update();
}
}, 500);
} else {
event.data.editor.update();
}
}
},
submitHandler : function(event) {
event.data.editor.userClicked = true;
clearTimeout(event.data.editor.blurTimer);
event.data.editor.update();
},
cancelButtonHandler : function(event) {
event.data.editor.userClicked = true;
clearTimeout(event.data.editor.blurTimer);
event.data.editor.abortIfConfirm();
event.stopPropagation(); // Without this, click isn't handled
},
// START METAMAPS CODE
keydownHandler : function(event) {
if (event.keyCode == 13 && !event.shiftKey) {
event.preventDefault();
event.stopPropagation();
return false;
}
},
// END METAMAPS CODE
keyupHandler : function(event) {
if (event.keyCode == 27) {
event.data.editor.abortIfConfirm();
}
// START METAMAPS CODE
else if (event.keyCode == 13 && !event.shiftKey) {
event.data.editor.update();
}
// END METAMAPS CODE
}
}
};
jQuery.fn.best_in_place = function() {
function setBestInPlace(element) {
if (!element.data('bestInPlaceEditor')) {
element.data('bestInPlaceEditor', new BestInPlaceEditor(element));
return true;
}
}
jQuery(this.context).delegate(this.selector, 'click', function () {
var el = jQuery(this);
if (setBestInPlace(el))
el.click();
});
this.each(function () {
setBestInPlace(jQuery(this));
});
return this;
};
/**
* @name Elastic
* @descripton Elastic is Jquery plugin that grow and shrink your textareas automaticliy
* @version 1.6.5
* @requires Jquery 1.2.6+
*
* @author Jan Jarfalk
* @author-email jan.jarfalk@unwrongest.com
* @author-website http://www.unwrongest.com
*
* @licens MIT License - http://www.opensource.org/licenses/mit-license.php
*/
(function(jQuery){
if (typeof jQuery.fn.elastic !== 'undefined') return;
jQuery.fn.extend({
elastic: function() {
// We will create a div clone of the textarea
// by copying these attributes from the textarea to the div.
var mimics = [
'paddingTop',
'paddingRight',
'paddingBottom',
'paddingLeft',
'fontSize',
'lineHeight',
'fontFamily',
'width',
'fontWeight'];
return this.each( function() {
// Elastic only works on textareas
if ( this.type != 'textarea' ) {
return false;
}
var $textarea = jQuery(this),
$twin = jQuery('<div />').css({'position': 'absolute','display':'none','word-wrap':'break-word'}),
lineHeight = parseInt($textarea.css('line-height'),10) || parseInt($textarea.css('font-size'),'10'),
minheight = parseInt($textarea.css('height'),10) || lineHeight*3,
maxheight = parseInt($textarea.css('max-height'),10) || Number.MAX_VALUE,
goalheight = 0,
i = 0;
// Opera returns max-height of -1 if not set
if (maxheight < 0) { maxheight = Number.MAX_VALUE; }
// Append the twin to the DOM
// We are going to meassure the height of this, not the textarea.
$twin.appendTo($textarea.parent());
// Copy the essential styles (mimics) from the textarea to the twin
i = mimics.length;
while(i--){
$twin.css(mimics[i].toString(),$textarea.css(mimics[i].toString()));
}
// Sets a given height and overflow state on the textarea
function setHeightAndOverflow(height, overflow){
curratedHeight = Math.floor(parseInt(height,10));
if($textarea.height() != curratedHeight){
$textarea.css({'height': curratedHeight + 'px','overflow':overflow});
}
}
// This function will update the height of the textarea if necessary
function update() {
// Get curated content from the textarea.
var textareaContent = $textarea.val().replace(/&/g,'&amp;').replace(/ /g, '&nbsp;').replace(/<|>/g, '&gt;').replace(/\n/g, '<br />');
// Compare curated content with curated twin.
var twinContent = $twin.html().replace(/<br>/ig,'<br />');
if(textareaContent+'&nbsp;' != twinContent){
// Add an extra white space so new rows are added when you are at the end of a row.
$twin.html(textareaContent+'&nbsp;');
// Change textarea height if twin plus the height of one line differs more than 3 pixel from textarea height
if(Math.abs($twin.height() + lineHeight - $textarea.height()) > 3){
var goalheight = $twin.height()+lineHeight;
if(goalheight >= maxheight) {
setHeightAndOverflow(maxheight,'auto');
} else if(goalheight <= minheight) {
setHeightAndOverflow(minheight,'hidden');
} else {
setHeightAndOverflow(goalheight,'hidden');
}
}
}
}
// Hide scrollbars
$textarea.css({'overflow':'hidden'});
// Update textarea size on keyup, change, cut and paste
$textarea.bind('keyup change cut paste', function(){
update();
});
// Compact textarea on blur
// Lets animate this....
$textarea.bind('blur',function(){
if($twin.height() < maxheight){
if($twin.height() > minheight) {
$textarea.height($twin.height());
} else {
$textarea.height(minheight);
}
}
});
// And this line is to catch the browser paste event
$textarea.on("input paste", function(e){ setTimeout( update, 250); });
// Run update once when elastic is initialized
update();
});
}
});
})(jQuery);

View file

@ -1,2 +0,0 @@
!function(t){"use strict";var e=t.HTMLCanvasElement&&t.HTMLCanvasElement.prototype,o=t.Blob&&function(){try{return Boolean(new Blob)}catch(t){return!1}}(),n=o&&t.Uint8Array&&function(){try{return 100===new Blob([new Uint8Array(100)]).size}catch(t){return!1}}(),r=t.BlobBuilder||t.WebKitBlobBuilder||t.MozBlobBuilder||t.MSBlobBuilder,a=/^data:((.*?)(;charset=.*?)?)(;base64)?,/,i=(o||r)&&t.atob&&t.ArrayBuffer&&t.Uint8Array&&function(t){var e,i,l,u,b,c,d,B,f;if(e=t.match(a),!e)throw new Error("invalid data URI");for(i=e[2]?e[1]:"text/plain"+(e[3]||";charset=US-ASCII"),l=!!e[4],u=t.slice(e[0].length),b=l?atob(u):decodeURIComponent(u),c=new ArrayBuffer(b.length),d=new Uint8Array(c),B=0;B<b.length;B+=1)d[B]=b.charCodeAt(B);return o?new Blob([n?d:c],{type:i}):(f=new r,f.append(c),f.getBlob(i))};t.HTMLCanvasElement&&!e.toBlob&&(e.mozGetAsFile?e.toBlob=function(t,o,n){t(n&&e.toDataURL&&i?i(this.toDataURL(o,n)):this.mozGetAsFile("blob",o))}:e.toDataURL&&i&&(e.toBlob=function(t,e,o){t(i(this.toDataURL(e,o)))})),"function"==typeof define&&define.amd?define(function(){return i}):"object"==typeof module&&module.exports?module.exports=i:t.dataURLtoBlob=i}(window);
//# sourceMappingURL=canvas-to-blob.min.js.map

View file

@ -1,431 +1,426 @@
//////////////////////////////////////////////////////////////////////////////////
// CloudCarousel V1.0.5
// (c) 2011 by R Cecco. <http://www.professorcloud.com>
// MIT License
//
// Reflection code based on plugin by Christophe Beyls <http://www.digitalia.be>
//
// Please retain this copyright header in all versions of the software
//////////////////////////////////////////////////////////////////////////////////
var matched, browser;
jQuery.uaMatch = function( ua ) {
ua = ua.toLowerCase();
var match = /(chrome)[ \/]([\w.]+)/.exec( ua ) ||
/(webkit)[ \/]([\w.]+)/.exec( ua ) ||
/(opera)(?:.*version|)[ \/]([\w.]+)/.exec( ua ) ||
/(msie) ([\w.]+)/.exec( ua ) ||
ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec( ua ) ||
[];
return {
browser: match[ 1 ] || "",
version: match[ 2 ] || "0"
};
};
matched = jQuery.uaMatch( navigator.userAgent );
browser = {};
if ( matched.browser ) {
browser[ matched.browser ] = true;
browser.version = matched.version;
}
// Chrome is Webkit, but Webkit is also Safari.
if ( browser.chrome ) {
browser.webkit = true;
} else if ( browser.webkit ) {
browser.safari = true;
}
jQuery.browser = browser;
(function($) {
// START Reflection object.
// Creates a reflection for underneath an image.
// IE uses an image with IE specific filter properties, other browsers use the Canvas tag.
// The position and size of the reflection gets updated by updateAll() in Controller.
function Reflection(img, reflHeight, opacity) {
var reflection, cntx, imageWidth = img.width, imageHeight = img.width, gradient, parent;
parent = $(img.parentNode);
this.element = reflection = parent.append("<canvas class='reflection' style='position:absolute'/>").find(':last')[0];
if ( !reflection.getContext && $.browser.msie) {
this.element = reflection = parent.append("<img class='reflection' style='position:absolute'/>").find(':last')[0];
reflection.src = img.src;
reflection.style.filter = "flipv progid:DXImageTransform.Microsoft.Alpha(opacity=" + (opacity * 100) + ", style=1, finishOpacity=0, startx=0, starty=0, finishx=0, finishy=" + (reflHeight / imageHeight * 100) + ")";
} else {
cntx = reflection.getContext("2d");
try {
$(reflection).attr({width: imageWidth, height: reflHeight});
cntx.save();
cntx.translate(0, imageHeight-1);
cntx.scale(1, -1);
cntx.drawImage(img, 0, 0, imageWidth, imageHeight);
cntx.restore();
cntx.globalCompositeOperation = "destination-out";
gradient = cntx.createLinearGradient(0, 0, 0, reflHeight);
gradient.addColorStop(0, "rgba(255, 255, 255, " + (1 - opacity) + ")");
gradient.addColorStop(1, "rgba(255, 255, 255, 1.0)");
cntx.fillStyle = gradient;
cntx.fillRect(0, 0, imageWidth, reflHeight);
} catch(e) {
return;
}
}
// Store a copy of the alt and title attrs into the reflection
$(reflection).attr({ 'alt': $(img).attr('alt'), title: $(img).attr('title')} );
} //END Reflection object
// START Item object.
// A wrapper object for items within the carousel.
var Item = function(imgIn, options)
{
this.orgWidth = imgIn.width;
this.orgHeight = imgIn.height;
this.image = imgIn;
this.reflection = null;
this.alt = imgIn.alt;
this.title = imgIn.title;
this.imageOK = false;
this.options = options;
this.imageOK = true;
if (this.options.reflHeight > 0)
{
this.reflection = new Reflection(this.image, this.options.reflHeight, this.options.reflOpacity);
}
$(this.image).css('position','absolute'); // Bizarre. This seems to reset image width to 0 on webkit!
};// END Item object
// Controller object.
// This handles moving all the items, dealing with mouse clicks etc.
var Controller = function(container, images, options)
{
var items = [], funcSin = Math.sin, funcCos = Math.cos, ctx=this;
this.controlTimer = 0;
this.stopped = false;
//this.imagesLoaded = 0;
this.container = container;
this.xRadius = options.xRadius;
this.yRadius = options.yRadius;
this.showFrontTextTimer = 0;
this.autoRotateTimer = 0;
if (options.xRadius === 0)
{
this.xRadius = ($(container).width()/2.3);
}
if (options.yRadius === 0)
{
this.yRadius = ($(container).height()/6);
}
this.xCentre = options.xPos;
this.yCentre = options.yPos;
this.frontIndex = 0; // Index of the item at the front
// Start with the first item at the front.
this.rotation = this.destRotation = Math.PI/2;
this.timeDelay = 1000/options.FPS;
// Turn on the infoBox
if(options.altBox !== null)
{
$(options.altBox).css('display','block');
$(options.titleBox).css('display','block');
}
// Turn on relative position for container to allow absolutely positioned elements
// within it to work.
$(container).css({ position:'relative', overflow:'hidden'} );
$(options.buttonLeft).css('display','inline');
$(options.buttonRight).css('display','inline');
// Setup the buttons.
$(options.buttonLeft).bind('mouseup',this,function(event){
event.data.rotate(-1);
return false;
});
$(options.buttonRight).bind('mouseup',this,function(event){
event.data.rotate(1);
return false;
});
// START METAMAPS CODE
// Add code that makes tab and shift+tab scroll through metacodes
$('.new_topic').bind('keydown',this,function(event){
if (event.keyCode == 9) {
if (event.shiftKey) {
event.data.rotate(-1)
} else {
event.data.rotate(1)
}
event.preventDefault();
event.stopPropagation();
Metamaps.Create.newTopic.metacode = $(items[event.data.frontIndex].image).attr('data-id');
}
});
// END METAMAPS CODE
// You will need this plugin for the mousewheel to work: http://plugins.jquery.com/project/mousewheel
if (options.mouseWheel)
{
// START METAMAPS CODE
$('body').bind('mousewheel',this,function(event, delta) {
if (Metamaps.Create.newTopic.beingCreated &&
!Metamaps.Create.isSwitchingSet &&
!Metamaps.Create.newTopic.pinned) {
event.data.rotate(delta);
return false;
}
});
// END METAMAPS CODE
// ORIGINAL CODE
// $(container).bind('mousewheel',this,function(event, delta) {
// event.data.rotate(delta);
// return false;
// });
//
}
$(container).unbind('mouseover click').bind('mouseover click',this,function(event){
clearInterval(event.data.autoRotateTimer); // Stop auto rotation if mouse over.
var text = $(event.target).attr('alt');
// If we have moved over a carousel item, then show the alt and title text.
if ( text !== undefined && text !== null )
{
clearTimeout(event.data.showFrontTextTimer);
$(options.altBox).html( ($(event.target).attr('alt') ));
//$(options.titleBox).html( ($(event.target).attr('title') ));
if ( options.bringToFront && event.type == 'click' )
{
$(options.titleBox).html( ($(event.target).attr('title') ));
// START METAMAPS CODE
Metamaps.Create.newTopic.metacode = $(event.target).attr('data-id');
// END METAMAPS CODE
var idx = $(event.target).data('itemIndex');
var frontIndex = event.data.frontIndex;
//var diff = idx - frontIndex;
var diff = (idx - frontIndex) % images.length;
if (Math.abs(diff) > images.length / 2) {
diff += (diff > 0 ? -images.length : images.length);
}
event.data.rotate(-diff);
}
}
});
// START METAMAPS CODE - initialize newTopic.metacode
var first = $(this.container).find('img').get(0)
Metamaps.Create.newTopic.metacode = $(first).data('id')
// END METAMAPS CODE
// If we have moved out of a carousel item (or the container itself),
// restore the text of the front item in 1 second.
$(container).bind('mouseout',this,function(event){
var context = event.data;
clearTimeout(context.showFrontTextTimer);
context.showFrontTextTimer = setTimeout( function(){context.showFrontText();},1000);
context.autoRotate(); // Start auto rotation.
});
// Prevent items from being selected as mouse is moved and clicked in the container.
$(container).bind('mousedown',this,function(event){
event.data.container.focus();
return false;
});
container.onselectstart = function () { return false; }; // For IE.
this.innerWrapper = $(container).wrapInner('<div style="position:absolute;width:100%;height:100%;"/>').children()[0];
// Shows the text from the front most item.
this.showFrontText = function()
{
if ( items[this.frontIndex] === undefined ) { return; } // Images might not have loaded yet.
$(options.titleBox).html( $(items[this.frontIndex].image).attr('title'));
$(options.altBox).html( $(items[this.frontIndex].image).attr('alt'));
};
this.go = function()
{
if(this.controlTimer !== 0) { return; }
var context = this;
this.controlTimer = setTimeout( function(){context.updateAll();},this.timeDelay);
};
this.stop = function()
{
clearTimeout(this.controlTimer);
this.controlTimer = 0;
};
// Starts the rotation of the carousel. Direction is the number (+-) of carousel items to rotate by.
this.rotate = function(direction)
{
this.frontIndex -= direction;
if (this.frontIndex == -1) this.frontIndex = items.length - 1;
this.frontIndex %= items.length;
this.destRotation += ( Math.PI / items.length ) * ( 2*direction );
this.showFrontText();
this.go();
};
this.autoRotate = function()
{
if ( options.autoRotate !== 'no' )
{
var dir = (options.autoRotate === 'right')? 1 : -1;
this.autoRotateTimer = setInterval( function(){ctx.rotate(dir); }, options.autoRotateDelay );
}
};
// This is the main loop function that moves everything.
this.updateAll = function()
{
var minScale = options.minScale; // This is the smallest scale applied to the furthest item.
var smallRange = (1-minScale) * 0.5;
var w,h,x,y,scale,item,sinVal;
var change = (this.destRotation - this.rotation);
var absChange = Math.abs(change);
this.rotation += change * options.speed;
if ( absChange < 0.001 ) { this.rotation = this.destRotation; }
var itemsLen = items.length;
var spacing = (Math.PI / itemsLen) * 2;
//var wrapStyle = null;
var radians = this.rotation;
var isMSIE = $.browser.msie;
// Turn off display. This can reduce repaints/reflows when making style and position changes in the loop.
// See http://dev.opera.com/articles/view/efficient-javascript/?page=3
this.innerWrapper.style.display = 'none';
var style;
var px = 'px', reflHeight;
var context = this;
for (var i = 0; i<itemsLen ;i++)
{
item = items[i];
sinVal = funcSin(radians);
scale = ((sinVal+1) * smallRange) + minScale;
x = this.xCentre + (( (funcCos(radians) * this.xRadius) - (item.orgWidth*0.5)) * scale);
y = this.yCentre + (( (sinVal * this.yRadius) ) * scale);
if (item.imageOK)
{
var img = item.image;
img.style.zIndex = "" + (scale * 100)>>0; // >>0 = Math.foor(). Firefox doesn't like fractional decimals in z-index.
w = img.width = item.orgWidth * scale;
h = img.height = item.orgHeight * scale;
img.style.left = x + px ;
img.style.top = y + px;
if (item.reflection !== null)
{
reflHeight = options.reflHeight * scale;
style = item.reflection.element.style;
style.left = x + px;
style.top = y + h + options.reflGap * scale + px;
style.width = w + px;
if (isMSIE)
{
style.filter.finishy = (reflHeight / h * 100);
}else
{
style.height = reflHeight + px;
}
}
}
radians += spacing;
}
// Turn display back on.
this.innerWrapper.style.display = 'block';
// If we have a preceptable change in rotation then loop again next frame.
if ( absChange >= 0.001 )
{
this.controlTimer = setTimeout( function(){context.updateAll();},this.timeDelay);
}else
{
// Otherwise just stop completely.
this.stop();
}
}; // END updateAll
// Create an Item object for each image
// func = function(){return;ctx.updateAll();} ;
// Check if images have loaded. We need valid widths and heights for the reflections.
this.checkImagesLoaded = function()
{
var i;
for(i=0;i<images.length;i++) {
if ( (images[i].width === undefined) || ( (images[i].complete !== undefined) && (!images[i].complete) ))
{
return;
}
}
for(i=0;i<images.length;i++) {
items.push( new Item( images[i], options ) );
$(images[i]).data('itemIndex',i);
}
// If all images have valid widths and heights, we can stop checking.
clearInterval(this.tt);
this.showFrontText();
this.autoRotate();
this.updateAll();
};
this.tt = setInterval( function(){ctx.checkImagesLoaded();},50);
}; // END Controller object
// The jQuery plugin part. Iterates through items specified in selector and inits a Controller class for each one.
$.fn.CloudCarousel = function(options) {
this.each( function() {
options = $.extend({}, {
reflHeight:0,
reflOpacity:0.5,
reflGap:0,
minScale:0.5,
xPos:0,
yPos:0,
xRadius:0,
yRadius:0,
altBox:null,
titleBox:null,
FPS: 30,
autoRotate: 'no',
autoRotateDelay: 1500,
speed:0.2,
mouseWheel: false,
bringToFront: false
},options );
// Create a Controller for each carousel.
$(this).data('cloudcarousel', new Controller( this, $('.cloudcarousel',$(this)), options) );
});
return this;
};
})(jQuery);
//////////////////////////////////////////////////////////////////////////////////
// CloudCarousel V1.0.5
// (c) 2011 by R Cecco. <http://www.professorcloud.com>
// MIT License
//
// Reflection code based on plugin by Christophe Beyls <http://www.digitalia.be>
//
// Please retain this copyright header in all versions of the software
//////////////////////////////////////////////////////////////////////////////////
var matched, browser;
jQuery.uaMatch = function( ua ) {
ua = ua.toLowerCase();
var match = /(chrome)[ \/]([\w.]+)/.exec( ua ) ||
/(webkit)[ \/]([\w.]+)/.exec( ua ) ||
/(opera)(?:.*version|)[ \/]([\w.]+)/.exec( ua ) ||
/(msie) ([\w.]+)/.exec( ua ) ||
ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec( ua ) ||
[];
return {
browser: match[ 1 ] || "",
version: match[ 2 ] || "0"
};
};
matched = jQuery.uaMatch( navigator.userAgent );
browser = {};
if ( matched.browser ) {
browser[ matched.browser ] = true;
browser.version = matched.version;
}
// Chrome is Webkit, but Webkit is also Safari.
if ( browser.chrome ) {
browser.webkit = true;
} else if ( browser.webkit ) {
browser.safari = true;
}
jQuery.browser = browser;
(function($) {
// START Reflection object.
// Creates a reflection for underneath an image.
// IE uses an image with IE specific filter properties, other browsers use the Canvas tag.
// The position and size of the reflection gets updated by updateAll() in Controller.
function Reflection(img, reflHeight, opacity) {
var reflection, cntx, imageWidth = img.width, imageHeight = img.width, gradient, parent;
parent = $(img.parentNode);
this.element = reflection = parent.append("<canvas class='reflection' style='position:absolute'/>").find(':last')[0];
if ( !reflection.getContext && $.browser.msie) {
this.element = reflection = parent.append("<img class='reflection' style='position:absolute'/>").find(':last')[0];
reflection.src = img.src;
reflection.style.filter = "flipv progid:DXImageTransform.Microsoft.Alpha(opacity=" + (opacity * 100) + ", style=1, finishOpacity=0, startx=0, starty=0, finishx=0, finishy=" + (reflHeight / imageHeight * 100) + ")";
} else {
cntx = reflection.getContext("2d");
try {
$(reflection).attr({width: imageWidth, height: reflHeight});
cntx.save();
cntx.translate(0, imageHeight-1);
cntx.scale(1, -1);
cntx.drawImage(img, 0, 0, imageWidth, imageHeight);
cntx.restore();
cntx.globalCompositeOperation = "destination-out";
gradient = cntx.createLinearGradient(0, 0, 0, reflHeight);
gradient.addColorStop(0, "rgba(255, 255, 255, " + (1 - opacity) + ")");
gradient.addColorStop(1, "rgba(255, 255, 255, 1.0)");
cntx.fillStyle = gradient;
cntx.fillRect(0, 0, imageWidth, reflHeight);
} catch(e) {
return;
}
}
// Store a copy of the alt and title attrs into the reflection
$(reflection).attr({ 'alt': $(img).attr('alt'), title: $(img).attr('title')} );
} //END Reflection object
// START Item object.
// A wrapper object for items within the carousel.
var Item = function(imgIn, options)
{
this.orgWidth = imgIn.width;
this.orgHeight = imgIn.height;
this.image = imgIn;
this.reflection = null;
this.alt = imgIn.alt;
this.title = imgIn.title;
this.imageOK = false;
this.options = options;
this.imageOK = true;
if (this.options.reflHeight > 0)
{
this.reflection = new Reflection(this.image, this.options.reflHeight, this.options.reflOpacity);
}
$(this.image).css('position','absolute'); // Bizarre. This seems to reset image width to 0 on webkit!
};// END Item object
// Controller object.
// This handles moving all the items, dealing with mouse clicks etc.
var Controller = function(container, images, options)
{
var items = [], funcSin = Math.sin, funcCos = Math.cos, ctx=this;
this.controlTimer = 0;
this.stopped = false;
//this.imagesLoaded = 0;
this.container = container;
this.xRadius = options.xRadius;
this.yRadius = options.yRadius;
this.showFrontTextTimer = 0;
this.autoRotateTimer = 0;
if (options.xRadius === 0)
{
this.xRadius = ($(container).width()/2.3);
}
if (options.yRadius === 0)
{
this.yRadius = ($(container).height()/6);
}
this.xCentre = options.xPos;
this.yCentre = options.yPos;
this.frontIndex = 0; // Index of the item at the front
// Start with the first item at the front.
this.rotation = this.destRotation = Math.PI/2;
this.timeDelay = 1000/options.FPS;
// Turn on the infoBox
if(options.altBox !== null)
{
$(options.altBox).css('display','block');
$(options.titleBox).css('display','block');
}
// Turn on relative position for container to allow absolutely positioned elements
// within it to work.
$(container).css({ position:'relative', overflow:'hidden'} );
$(options.buttonLeft).css('display','inline');
$(options.buttonRight).css('display','inline');
// Setup the buttons.
$(options.buttonLeft).bind('mouseup',this,function(event){
event.data.rotate(-1);
return false;
});
$(options.buttonRight).bind('mouseup',this,function(event){
event.data.rotate(1);
return false;
});
// Add code that makes tab and shift+tab scroll through metacodes
$('.new_topic').bind('keydown',this,function(event){
if (event.keyCode == 9 && event.shiftKey) {
event.data.rotate(-1);
event.preventDefault();
event.stopPropagation();
} else if (event.keyCode == 9) {
event.data.rotate(1);
event.preventDefault();
event.stopPropagation();
}
});
// You will need this plugin for the mousewheel to work: http://plugins.jquery.com/project/mousewheel
if (options.mouseWheel)
{
// START METAMAPS CODE
$('body').bind('mousewheel',this,function(event, delta) {
if (Metamaps.Create.newTopic.beingCreated && !Metamaps.Create.isSwitchingSet) {
event.data.rotate(delta);
return false;
}
});
// END METAMAPS CODE
/* ORIGINAL CODE
$(container).bind('mousewheel',this,function(event, delta) {
event.data.rotate(delta);
return false;
});
*/
}
$(container).bind('mouseover click',this,function(event){
clearInterval(event.data.autoRotateTimer); // Stop auto rotation if mouse over.
var text = $(event.target).attr('alt');
// If we have moved over a carousel item, then show the alt and title text.
if ( text !== undefined && text !== null )
{
clearTimeout(event.data.showFrontTextTimer);
$(options.altBox).html( ($(event.target).attr('alt') ));
//$(options.titleBox).html( ($(event.target).attr('title') ));
if ( options.bringToFront && event.type == 'click' )
{
$(options.titleBox).html( ($(event.target).attr('title') ));
// METAMAPS CODE
Metamaps.Create.newTopic.metacode = $(event.target).attr('data-id');
// NOT METAMAPS CODE
var idx = $(event.target).data('itemIndex');
var frontIndex = event.data.frontIndex;
//var diff = idx - frontIndex;
var diff = (idx - frontIndex) % images.length;
if (Math.abs(diff) > images.length / 2) {
diff += (diff > 0 ? -images.length : images.length);
}
event.data.rotate(-diff);
}
}
});
// If we have moved out of a carousel item (or the container itself),
// restore the text of the front item in 1 second.
$(container).bind('mouseout',this,function(event){
var context = event.data;
clearTimeout(context.showFrontTextTimer);
context.showFrontTextTimer = setTimeout( function(){context.showFrontText();},1000);
context.autoRotate(); // Start auto rotation.
});
// Prevent items from being selected as mouse is moved and clicked in the container.
$(container).bind('mousedown',this,function(event){
event.data.container.focus();
return false;
});
container.onselectstart = function () { return false; }; // For IE.
this.innerWrapper = $(container).wrapInner('<div style="position:absolute;width:100%;height:100%;"/>').children()[0];
// Shows the text from the front most item.
this.showFrontText = function()
{
if ( items[this.frontIndex] === undefined ) { return; } // Images might not have loaded yet.
// METAMAPS CODE
Metamaps.Create.newTopic.metacode = $(items[this.frontIndex].image).attr('data-id');
//$('img.cloudcarousel').css({"background":"none", "width":"","height":""});
//$(items[this.frontIndex].image).css({"width":"45px","height":"45px"});
// NOT METAMAPS CODE
$(options.titleBox).html( $(items[this.frontIndex].image).attr('title'));
$(options.altBox).html( $(items[this.frontIndex].image).attr('alt'));
};
this.go = function()
{
if(this.controlTimer !== 0) { return; }
var context = this;
this.controlTimer = setTimeout( function(){context.updateAll();},this.timeDelay);
};
this.stop = function()
{
clearTimeout(this.controlTimer);
this.controlTimer = 0;
};
// Starts the rotation of the carousel. Direction is the number (+-) of carousel items to rotate by.
this.rotate = function(direction)
{
this.frontIndex -= direction;
if (this.frontIndex == -1) this.frontIndex = items.length - 1;
this.frontIndex %= items.length;
this.destRotation += ( Math.PI / items.length ) * ( 2*direction );
this.showFrontText();
this.go();
};
this.autoRotate = function()
{
if ( options.autoRotate !== 'no' )
{
var dir = (options.autoRotate === 'right')? 1 : -1;
this.autoRotateTimer = setInterval( function(){ctx.rotate(dir); }, options.autoRotateDelay );
}
};
// This is the main loop function that moves everything.
this.updateAll = function()
{
var minScale = options.minScale; // This is the smallest scale applied to the furthest item.
var smallRange = (1-minScale) * 0.5;
var w,h,x,y,scale,item,sinVal;
var change = (this.destRotation - this.rotation);
var absChange = Math.abs(change);
this.rotation += change * options.speed;
if ( absChange < 0.001 ) { this.rotation = this.destRotation; }
var itemsLen = items.length;
var spacing = (Math.PI / itemsLen) * 2;
//var wrapStyle = null;
var radians = this.rotation;
var isMSIE = $.browser.msie;
// Turn off display. This can reduce repaints/reflows when making style and position changes in the loop.
// See http://dev.opera.com/articles/view/efficient-javascript/?page=3
this.innerWrapper.style.display = 'none';
var style;
var px = 'px', reflHeight;
var context = this;
for (var i = 0; i<itemsLen ;i++)
{
item = items[i];
sinVal = funcSin(radians);
scale = ((sinVal+1) * smallRange) + minScale;
x = this.xCentre + (( (funcCos(radians) * this.xRadius) - (item.orgWidth*0.5)) * scale);
y = this.yCentre + (( (sinVal * this.yRadius) ) * scale);
if (item.imageOK)
{
var img = item.image;
img.style.zIndex = "" + (scale * 100)>>0; // >>0 = Math.foor(). Firefox doesn't like fractional decimals in z-index.
w = img.width = item.orgWidth * scale;
h = img.height = item.orgHeight * scale;
img.style.left = x + px ;
img.style.top = y + px;
if (item.reflection !== null)
{
reflHeight = options.reflHeight * scale;
style = item.reflection.element.style;
style.left = x + px;
style.top = y + h + options.reflGap * scale + px;
style.width = w + px;
if (isMSIE)
{
style.filter.finishy = (reflHeight / h * 100);
}else
{
style.height = reflHeight + px;
}
}
}
radians += spacing;
}
// Turn display back on.
this.innerWrapper.style.display = 'block';
// If we have a preceptable change in rotation then loop again next frame.
if ( absChange >= 0.001 )
{
this.controlTimer = setTimeout( function(){context.updateAll();},this.timeDelay);
}else
{
// Otherwise just stop completely.
this.stop();
}
}; // END updateAll
// Create an Item object for each image
// func = function(){return;ctx.updateAll();} ;
// Check if images have loaded. We need valid widths and heights for the reflections.
this.checkImagesLoaded = function()
{
var i;
for(i=0;i<images.length;i++) {
if ( (images[i].width === undefined) || ( (images[i].complete !== undefined) && (!images[i].complete) ))
{
return;
}
}
for(i=0;i<images.length;i++) {
items.push( new Item( images[i], options ) );
$(images[i]).data('itemIndex',i);
}
// If all images have valid widths and heights, we can stop checking.
clearInterval(this.tt);
this.showFrontText();
this.autoRotate();
this.updateAll();
};
this.tt = setInterval( function(){ctx.checkImagesLoaded();},50);
}; // END Controller object
// The jQuery plugin part. Iterates through items specified in selector and inits a Controller class for each one.
$.fn.CloudCarousel = function(options) {
this.each( function() {
options = $.extend({}, {
reflHeight:0,
reflOpacity:0.5,
reflGap:0,
minScale:0.5,
xPos:0,
yPos:0,
xRadius:0,
yRadius:0,
altBox:null,
titleBox:null,
FPS: 30,
autoRotate: 'no',
autoRotateDelay: 1500,
speed:0.2,
mouseWheel: false,
bringToFront: false
},options );
// Create a Controller for each carousel.
$(this).data('cloudcarousel', new Controller( this, $('.cloudcarousel',$(this)), options) );
});
return this;
};
})(jQuery);

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,180 @@
/**
* jquery.purr.js
* Copyright (c) 2008 Net Perspective (net-perspective.com)
* Licensed under the MIT License (http://www.opensource.org/licenses/mit-license.php)
*
* @author R.A. Ray
* @projectDescription jQuery plugin for dynamically displaying unobtrusive messages in the browser. Mimics the behavior of the MacOS program "Growl."
* @version 0.1.0
*
* @requires jquery.js (tested with 1.2.6)
*
* @param fadeInSpeed int - Duration of fade in animation in miliseconds
* default: 500
* @param fadeOutSpeed int - Duration of fade out animationin miliseconds
default: 500
* @param removeTimer int - Timeout, in miliseconds, before notice is removed once it is the top non-sticky notice in the list
default: 4000
* @param isSticky bool - Whether the notice should fade out on its own or wait to be manually closed
default: false
* @param usingTransparentPNG bool - Whether or not the notice is using transparent .png images in its styling
default: false
*/
( function( $ ) {
$.purr = function ( notice, options )
{
// Convert notice to a jQuery object
notice = $( notice );
// Add a class to denote the notice as not sticky
if ( !options.isSticky )
{
notice.addClass( 'not-sticky' );
};
// Get the container element from the page
var cont = document.getElementById( 'purr-container' );
// If the container doesn't yet exist, we need to create it
if ( !cont )
{
cont = '<div id="purr-container"></div>';
}
// Convert cont to a jQuery object
cont = $( cont );
// Add the container to the page
$( 'body' ).append( cont );
notify();
function notify ()
{
// Set up the close button
var close = document.createElement( 'a' );
$( close ).attr(
{
className: 'close',
href: '#close',
innerHTML: 'Close'
}
)
.appendTo( notice )
.click( function ()
{
removeNotice();
return false;
}
);
// Add the notice to the page and keep it hidden initially
notice.appendTo( cont )
.hide();
if ( jQuery.browser.msie && options.usingTransparentPNG )
{
// IE7 and earlier can't handle the combination of opacity and transparent pngs, so if we're using transparent pngs in our
// notice style, we'll just skip the fading in.
notice.show();
}
else
{
//Fade in the notice we just added
notice.fadeIn( options.fadeInSpeed );
}
// Set up the removal interval for the added notice if that notice is not a sticky
if ( !options.isSticky )
{
var topSpotInt = setInterval( function ()
{
// Check to see if our notice is the first non-sticky notice in the list
if ( notice.prevAll( '.not-sticky' ).length == 0 )
{
// Stop checking once the condition is met
clearInterval( topSpotInt );
// Call the close action after the timeout set in options
setTimeout( function ()
{
removeNotice();
}, options.removeTimer
);
}
}, 200 );
}
}
function removeNotice ()
{
// IE7 and earlier can't handle the combination of opacity and transparent pngs, so if we're using transparent pngs in our
// notice style, we'll just skip the fading out.
if ( jQuery.browser.msie && options.usingTransparentPNG )
{
notice.css( { opacity: 0 } )
.animate(
{
height: '0px'
},
{
duration: options.fadeOutSpeed,
complete: function ()
{
notice.remove();
}
}
);
}
else
{
// Fade the object out before reducing its height to produce the sliding effect
notice.animate(
{
opacity: '0'
},
{
duration: options.fadeOutSpeed,
complete: function ()
{
notice.animate(
{
height: '0px'
},
{
duration: options.fadeOutSpeed,
complete: function ()
{
notice.remove();
}
}
);
}
}
);
}
};
};
$.fn.purr = function ( options )
{
options = options || {};
options.fadeInSpeed = options.fadeInSpeed || 500;
options.fadeOutSpeed = options.fadeOutSpeed || 500;
options.removeTimer = options.removeTimer || 4000;
options.isSticky = options.isSticky || false;
options.usingTransparentPNG = options.usingTransparentPNG || false;
this.each( function()
{
new $.purr( this, options );
}
);
return this;
};
})( jQuery );

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,41 @@
var USERVOICE;
if(USERVOICE == undefined) {
USERVOICE = {};
}
USERVOICE.load = function (name, id, email, sso_token) {
// Include the UserVoice JavaScript SDK (only needed once on a page)
UserVoice=window.UserVoice||[];(function(){var uv=document.createElement('script');uv.type='text/javascript';uv.async=true;uv.src='//widget.uservoice.com/wybK0nSMNuhlWkIKzTyWg.js';var s=document.getElementsByTagName('script')[0];s.parentNode.insertBefore(uv,s)})();
//
// UserVoice Javascript SDK developer documentation:
// https://www.uservoice.com/o/javascript-sdk
//
// Set colors
UserVoice.push(['set', {
accent_color: '#448dd6',
trigger_color: 'white',
trigger_background_color: 'rgba(46, 49, 51, 0.6)'
}]);
// Identify the user and pass traits
// To enable, replace sample data with actual user traits and uncomment the line
if (name) {
UserVoice.push(['setSSO', sso_token]);
UserVoice.push(['identify', {
'email': email, // Users email address
'name': name, // Users real name
'id': id, // Optional: Unique id of the user
}]);
}
// Add default trigger to the bottom-left corner of the window:
UserVoice.push(['addTrigger', { mode: 'contact', trigger_position: 'bottom-left' }]);
// Or, use your own custom trigger:
//UserVoice.push(['addTrigger', '#barometer_tab', { mode: 'contact' }]);
// Autoprompt for Satisfaction and SmartVote (only displayed under certain conditions)
UserVoice.push(['autoprompt', {}]);
};

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,36 @@
/*
RequireJS 2.1.11 Copyright (c) 2010-2014, The Dojo Foundation All Rights Reserved.
Available via the MIT or new BSD license.
see: http://github.com/jrburke/requirejs for details
*/
var requirejs,require,define;
(function(ca){function G(b){return"[object Function]"===M.call(b)}function H(b){return"[object Array]"===M.call(b)}function v(b,c){if(b){var d;for(d=0;d<b.length&&(!b[d]||!c(b[d],d,b));d+=1);}}function U(b,c){if(b){var d;for(d=b.length-1;-1<d&&(!b[d]||!c(b[d],d,b));d-=1);}}function s(b,c){return ga.call(b,c)}function j(b,c){return s(b,c)&&b[c]}function B(b,c){for(var d in b)if(s(b,d)&&c(b[d],d))break}function V(b,c,d,g){c&&B(c,function(c,h){if(d||!s(b,h))g&&"object"===typeof c&&c&&!H(c)&&!G(c)&&!(c instanceof
RegExp)?(b[h]||(b[h]={}),V(b[h],c,d,g)):b[h]=c});return b}function t(b,c){return function(){return c.apply(b,arguments)}}function da(b){throw b;}function ea(b){if(!b)return b;var c=ca;v(b.split("."),function(b){c=c[b]});return c}function C(b,c,d,g){c=Error(c+"\nhttp://requirejs.org/docs/errors.html#"+b);c.requireType=b;c.requireModules=g;d&&(c.originalError=d);return c}function ha(b){function c(a,e,b){var f,n,c,d,g,h,i,I=e&&e.split("/");n=I;var m=l.map,k=m&&m["*"];if(a&&"."===a.charAt(0))if(e){n=
I.slice(0,I.length-1);a=a.split("/");e=a.length-1;l.nodeIdCompat&&R.test(a[e])&&(a[e]=a[e].replace(R,""));n=a=n.concat(a);d=n.length;for(e=0;e<d;e++)if(c=n[e],"."===c)n.splice(e,1),e-=1;else if(".."===c)if(1===e&&(".."===n[2]||".."===n[0]))break;else 0<e&&(n.splice(e-1,2),e-=2);a=a.join("/")}else 0===a.indexOf("./")&&(a=a.substring(2));if(b&&m&&(I||k)){n=a.split("/");e=n.length;a:for(;0<e;e-=1){d=n.slice(0,e).join("/");if(I)for(c=I.length;0<c;c-=1)if(b=j(m,I.slice(0,c).join("/")))if(b=j(b,d)){f=b;
g=e;break a}!h&&(k&&j(k,d))&&(h=j(k,d),i=e)}!f&&h&&(f=h,g=i);f&&(n.splice(0,g,f),a=n.join("/"))}return(f=j(l.pkgs,a))?f:a}function d(a){z&&v(document.getElementsByTagName("script"),function(e){if(e.getAttribute("data-requiremodule")===a&&e.getAttribute("data-requirecontext")===i.contextName)return e.parentNode.removeChild(e),!0})}function g(a){var e=j(l.paths,a);if(e&&H(e)&&1<e.length)return e.shift(),i.require.undef(a),i.require([a]),!0}function u(a){var e,b=a?a.indexOf("!"):-1;-1<b&&(e=a.substring(0,
b),a=a.substring(b+1,a.length));return[e,a]}function m(a,e,b,f){var n,d,g=null,h=e?e.name:null,l=a,m=!0,k="";a||(m=!1,a="_@r"+(M+=1));a=u(a);g=a[0];a=a[1];g&&(g=c(g,h,f),d=j(p,g));a&&(g?k=d&&d.normalize?d.normalize(a,function(a){return c(a,h,f)}):c(a,h,f):(k=c(a,h,f),a=u(k),g=a[0],k=a[1],b=!0,n=i.nameToUrl(k)));b=g&&!d&&!b?"_unnormalized"+(Q+=1):"";return{prefix:g,name:k,parentMap:e,unnormalized:!!b,url:n,originalName:l,isDefine:m,id:(g?g+"!"+k:k)+b}}function q(a){var e=a.id,b=j(k,e);b||(b=k[e]=new i.Module(a));
return b}function r(a,e,b){var f=a.id,n=j(k,f);if(s(p,f)&&(!n||n.defineEmitComplete))"defined"===e&&b(p[f]);else if(n=q(a),n.error&&"error"===e)b(n.error);else n.on(e,b)}function w(a,e){var b=a.requireModules,f=!1;if(e)e(a);else if(v(b,function(e){if(e=j(k,e))e.error=a,e.events.error&&(f=!0,e.emit("error",a))}),!f)h.onError(a)}function x(){S.length&&(ia.apply(A,[A.length,0].concat(S)),S=[])}function y(a){delete k[a];delete W[a]}function F(a,e,b){var f=a.map.id;a.error?a.emit("error",a.error):(e[f]=
!0,v(a.depMaps,function(f,c){var d=f.id,g=j(k,d);g&&(!a.depMatched[c]&&!b[d])&&(j(e,d)?(a.defineDep(c,p[d]),a.check()):F(g,e,b))}),b[f]=!0)}function D(){var a,e,b=(a=1E3*l.waitSeconds)&&i.startTime+a<(new Date).getTime(),f=[],c=[],h=!1,k=!0;if(!X){X=!0;B(W,function(a){var i=a.map,m=i.id;if(a.enabled&&(i.isDefine||c.push(a),!a.error))if(!a.inited&&b)g(m)?h=e=!0:(f.push(m),d(m));else if(!a.inited&&(a.fetched&&i.isDefine)&&(h=!0,!i.prefix))return k=!1});if(b&&f.length)return a=C("timeout","Load timeout for modules: "+
f,null,f),a.contextName=i.contextName,w(a);k&&v(c,function(a){F(a,{},{})});if((!b||e)&&h)if((z||fa)&&!Y)Y=setTimeout(function(){Y=0;D()},50);X=!1}}function E(a){s(p,a[0])||q(m(a[0],null,!0)).init(a[1],a[2])}function K(a){var a=a.currentTarget||a.srcElement,e=i.onScriptLoad;a.detachEvent&&!Z?a.detachEvent("onreadystatechange",e):a.removeEventListener("load",e,!1);e=i.onScriptError;(!a.detachEvent||Z)&&a.removeEventListener("error",e,!1);return{node:a,id:a&&a.getAttribute("data-requiremodule")}}function L(){var a;
for(x();A.length;){a=A.shift();if(null===a[0])return w(C("mismatch","Mismatched anonymous define() module: "+a[a.length-1]));E(a)}}var X,$,i,N,Y,l={waitSeconds:7,baseUrl:"./",paths:{},bundles:{},pkgs:{},shim:{},config:{}},k={},W={},aa={},A=[],p={},T={},ba={},M=1,Q=1;N={require:function(a){return a.require?a.require:a.require=i.makeRequire(a.map)},exports:function(a){a.usingExports=!0;if(a.map.isDefine)return a.exports?p[a.map.id]=a.exports:a.exports=p[a.map.id]={}},module:function(a){return a.module?
a.module:a.module={id:a.map.id,uri:a.map.url,config:function(){return j(l.config,a.map.id)||{}},exports:a.exports||(a.exports={})}}};$=function(a){this.events=j(aa,a.id)||{};this.map=a;this.shim=j(l.shim,a.id);this.depExports=[];this.depMaps=[];this.depMatched=[];this.pluginMaps={};this.depCount=0};$.prototype={init:function(a,e,b,f){f=f||{};if(!this.inited){this.factory=e;if(b)this.on("error",b);else this.events.error&&(b=t(this,function(a){this.emit("error",a)}));this.depMaps=a&&a.slice(0);this.errback=
b;this.inited=!0;this.ignore=f.ignore;f.enabled||this.enabled?this.enable():this.check()}},defineDep:function(a,e){this.depMatched[a]||(this.depMatched[a]=!0,this.depCount-=1,this.depExports[a]=e)},fetch:function(){if(!this.fetched){this.fetched=!0;i.startTime=(new Date).getTime();var a=this.map;if(this.shim)i.makeRequire(this.map,{enableBuildCallback:!0})(this.shim.deps||[],t(this,function(){return a.prefix?this.callPlugin():this.load()}));else return a.prefix?this.callPlugin():this.load()}},load:function(){var a=
this.map.url;T[a]||(T[a]=!0,i.load(this.map.id,a))},check:function(){if(this.enabled&&!this.enabling){var a,e,b=this.map.id;e=this.depExports;var f=this.exports,c=this.factory;if(this.inited)if(this.error)this.emit("error",this.error);else{if(!this.defining){this.defining=!0;if(1>this.depCount&&!this.defined){if(G(c)){if(this.events.error&&this.map.isDefine||h.onError!==da)try{f=i.execCb(b,c,e,f)}catch(d){a=d}else f=i.execCb(b,c,e,f);this.map.isDefine&&void 0===f&&((e=this.module)?f=e.exports:this.usingExports&&
(f=this.exports));if(a)return a.requireMap=this.map,a.requireModules=this.map.isDefine?[this.map.id]:null,a.requireType=this.map.isDefine?"define":"require",w(this.error=a)}else f=c;this.exports=f;if(this.map.isDefine&&!this.ignore&&(p[b]=f,h.onResourceLoad))h.onResourceLoad(i,this.map,this.depMaps);y(b);this.defined=!0}this.defining=!1;this.defined&&!this.defineEmitted&&(this.defineEmitted=!0,this.emit("defined",this.exports),this.defineEmitComplete=!0)}}else this.fetch()}},callPlugin:function(){var a=
this.map,b=a.id,d=m(a.prefix);this.depMaps.push(d);r(d,"defined",t(this,function(f){var d,g;g=j(ba,this.map.id);var J=this.map.name,u=this.map.parentMap?this.map.parentMap.name:null,p=i.makeRequire(a.parentMap,{enableBuildCallback:!0});if(this.map.unnormalized){if(f.normalize&&(J=f.normalize(J,function(a){return c(a,u,!0)})||""),f=m(a.prefix+"!"+J,this.map.parentMap),r(f,"defined",t(this,function(a){this.init([],function(){return a},null,{enabled:!0,ignore:!0})})),g=j(k,f.id)){this.depMaps.push(f);
if(this.events.error)g.on("error",t(this,function(a){this.emit("error",a)}));g.enable()}}else g?(this.map.url=i.nameToUrl(g),this.load()):(d=t(this,function(a){this.init([],function(){return a},null,{enabled:!0})}),d.error=t(this,function(a){this.inited=!0;this.error=a;a.requireModules=[b];B(k,function(a){0===a.map.id.indexOf(b+"_unnormalized")&&y(a.map.id)});w(a)}),d.fromText=t(this,function(f,c){var g=a.name,J=m(g),k=O;c&&(f=c);k&&(O=!1);q(J);s(l.config,b)&&(l.config[g]=l.config[b]);try{h.exec(f)}catch(j){return w(C("fromtexteval",
"fromText eval for "+b+" failed: "+j,j,[b]))}k&&(O=!0);this.depMaps.push(J);i.completeLoad(g);p([g],d)}),f.load(a.name,p,d,l))}));i.enable(d,this);this.pluginMaps[d.id]=d},enable:function(){W[this.map.id]=this;this.enabling=this.enabled=!0;v(this.depMaps,t(this,function(a,b){var c,f;if("string"===typeof a){a=m(a,this.map.isDefine?this.map:this.map.parentMap,!1,!this.skipMap);this.depMaps[b]=a;if(c=j(N,a.id)){this.depExports[b]=c(this);return}this.depCount+=1;r(a,"defined",t(this,function(a){this.defineDep(b,
a);this.check()}));this.errback&&r(a,"error",t(this,this.errback))}c=a.id;f=k[c];!s(N,c)&&(f&&!f.enabled)&&i.enable(a,this)}));B(this.pluginMaps,t(this,function(a){var b=j(k,a.id);b&&!b.enabled&&i.enable(a,this)}));this.enabling=!1;this.check()},on:function(a,b){var c=this.events[a];c||(c=this.events[a]=[]);c.push(b)},emit:function(a,b){v(this.events[a],function(a){a(b)});"error"===a&&delete this.events[a]}};i={config:l,contextName:b,registry:k,defined:p,urlFetched:T,defQueue:A,Module:$,makeModuleMap:m,
nextTick:h.nextTick,onError:w,configure:function(a){a.baseUrl&&"/"!==a.baseUrl.charAt(a.baseUrl.length-1)&&(a.baseUrl+="/");var b=l.shim,c={paths:!0,bundles:!0,config:!0,map:!0};B(a,function(a,b){c[b]?(l[b]||(l[b]={}),V(l[b],a,!0,!0)):l[b]=a});a.bundles&&B(a.bundles,function(a,b){v(a,function(a){a!==b&&(ba[a]=b)})});a.shim&&(B(a.shim,function(a,c){H(a)&&(a={deps:a});if((a.exports||a.init)&&!a.exportsFn)a.exportsFn=i.makeShimExports(a);b[c]=a}),l.shim=b);a.packages&&v(a.packages,function(a){var b,
a="string"===typeof a?{name:a}:a;b=a.name;a.location&&(l.paths[b]=a.location);l.pkgs[b]=a.name+"/"+(a.main||"main").replace(ja,"").replace(R,"")});B(k,function(a,b){!a.inited&&!a.map.unnormalized&&(a.map=m(b))});if(a.deps||a.callback)i.require(a.deps||[],a.callback)},makeShimExports:function(a){return function(){var b;a.init&&(b=a.init.apply(ca,arguments));return b||a.exports&&ea(a.exports)}},makeRequire:function(a,e){function g(f,c,d){var j,l;e.enableBuildCallback&&(c&&G(c))&&(c.__requireJsBuild=
!0);if("string"===typeof f){if(G(c))return w(C("requireargs","Invalid require call"),d);if(a&&s(N,f))return N[f](k[a.id]);if(h.get)return h.get(i,f,a,g);j=m(f,a,!1,!0);j=j.id;return!s(p,j)?w(C("notloaded",'Module name "'+j+'" has not been loaded yet for context: '+b+(a?"":". Use require([])"))):p[j]}L();i.nextTick(function(){L();l=q(m(null,a));l.skipMap=e.skipMap;l.init(f,c,d,{enabled:!0});D()});return g}e=e||{};V(g,{isBrowser:z,toUrl:function(b){var e,d=b.lastIndexOf("."),g=b.split("/")[0];if(-1!==
d&&(!("."===g||".."===g)||1<d))e=b.substring(d,b.length),b=b.substring(0,d);return i.nameToUrl(c(b,a&&a.id,!0),e,!0)},defined:function(b){return s(p,m(b,a,!1,!0).id)},specified:function(b){b=m(b,a,!1,!0).id;return s(p,b)||s(k,b)}});a||(g.undef=function(b){x();var c=m(b,a,!0),e=j(k,b);d(b);delete p[b];delete T[c.url];delete aa[b];U(A,function(a,c){a[0]===b&&A.splice(c,1)});e&&(e.events.defined&&(aa[b]=e.events),y(b))});return g},enable:function(a){j(k,a.id)&&q(a).enable()},completeLoad:function(a){var b,
c,f=j(l.shim,a)||{},d=f.exports;for(x();A.length;){c=A.shift();if(null===c[0]){c[0]=a;if(b)break;b=!0}else c[0]===a&&(b=!0);E(c)}c=j(k,a);if(!b&&!s(p,a)&&c&&!c.inited){if(l.enforceDefine&&(!d||!ea(d)))return g(a)?void 0:w(C("nodefine","No define call for "+a,null,[a]));E([a,f.deps||[],f.exportsFn])}D()},nameToUrl:function(a,b,c){var f,d,g;(f=j(l.pkgs,a))&&(a=f);if(f=j(ba,a))return i.nameToUrl(f,b,c);if(h.jsExtRegExp.test(a))f=a+(b||"");else{f=l.paths;a=a.split("/");for(d=a.length;0<d;d-=1)if(g=a.slice(0,
d).join("/"),g=j(f,g)){H(g)&&(g=g[0]);a.splice(0,d,g);break}f=a.join("/");f+=b||(/^data\:|\?/.test(f)||c?"":".js");f=("/"===f.charAt(0)||f.match(/^[\w\+\.\-]+:/)?"":l.baseUrl)+f}return l.urlArgs?f+((-1===f.indexOf("?")?"?":"&")+l.urlArgs):f},load:function(a,b){h.load(i,a,b)},execCb:function(a,b,c,d){return b.apply(d,c)},onScriptLoad:function(a){if("load"===a.type||ka.test((a.currentTarget||a.srcElement).readyState))P=null,a=K(a),i.completeLoad(a.id)},onScriptError:function(a){var b=K(a);if(!g(b.id))return w(C("scripterror",
"Script error for: "+b.id,a,[b.id]))}};i.require=i.makeRequire();return i}var h,x,y,D,K,E,P,L,q,Q,la=/(\/\*([\s\S]*?)\*\/|([^:]|^)\/\/(.*)$)/mg,ma=/[^.]\s*require\s*\(\s*["']([^'"\s]+)["']\s*\)/g,R=/\.js$/,ja=/^\.\//;x=Object.prototype;var M=x.toString,ga=x.hasOwnProperty,ia=Array.prototype.splice,z=!!("undefined"!==typeof window&&"undefined"!==typeof navigator&&window.document),fa=!z&&"undefined"!==typeof importScripts,ka=z&&"PLAYSTATION 3"===navigator.platform?/^complete$/:/^(complete|loaded)$/,
Z="undefined"!==typeof opera&&"[object Opera]"===opera.toString(),F={},r={},S=[],O=!1;if("undefined"===typeof define){if("undefined"!==typeof requirejs){if(G(requirejs))return;r=requirejs;requirejs=void 0}"undefined"!==typeof require&&!G(require)&&(r=require,require=void 0);h=requirejs=function(b,c,d,g){var u,m="_";!H(b)&&"string"!==typeof b&&(u=b,H(c)?(b=c,c=d,d=g):b=[]);u&&u.context&&(m=u.context);(g=j(F,m))||(g=F[m]=h.s.newContext(m));u&&g.configure(u);return g.require(b,c,d)};h.config=function(b){return h(b)};
h.nextTick="undefined"!==typeof setTimeout?function(b){setTimeout(b,4)}:function(b){b()};require||(require=h);h.version="2.1.11";h.jsExtRegExp=/^\/|:|\?|\.js$/;h.isBrowser=z;x=h.s={contexts:F,newContext:ha};h({});v(["toUrl","undef","defined","specified"],function(b){h[b]=function(){var c=F._;return c.require[b].apply(c,arguments)}});if(z&&(y=x.head=document.getElementsByTagName("head")[0],D=document.getElementsByTagName("base")[0]))y=x.head=D.parentNode;h.onError=da;h.createNode=function(b){var c=
b.xhtml?document.createElementNS("http://www.w3.org/1999/xhtml","html:script"):document.createElement("script");c.type=b.scriptType||"text/javascript";c.charset="utf-8";c.async=!0;return c};h.load=function(b,c,d){var g=b&&b.config||{};if(z)return g=h.createNode(g,c,d),g.setAttribute("data-requirecontext",b.contextName),g.setAttribute("data-requiremodule",c),g.attachEvent&&!(g.attachEvent.toString&&0>g.attachEvent.toString().indexOf("[native code"))&&!Z?(O=!0,g.attachEvent("onreadystatechange",b.onScriptLoad)):
(g.addEventListener("load",b.onScriptLoad,!1),g.addEventListener("error",b.onScriptError,!1)),g.src=d,L=g,D?y.insertBefore(g,D):y.appendChild(g),L=null,g;if(fa)try{importScripts(d),b.completeLoad(c)}catch(j){b.onError(C("importscripts","importScripts failed for "+c+" at "+d,j,[c]))}};z&&!r.skipDataMain&&U(document.getElementsByTagName("script"),function(b){y||(y=b.parentNode);if(K=b.getAttribute("data-main"))return q=K,r.baseUrl||(E=q.split("/"),q=E.pop(),Q=E.length?E.join("/")+"/":"./",r.baseUrl=
Q),q=q.replace(R,""),h.jsExtRegExp.test(q)&&(q=K),r.deps=r.deps?r.deps.concat(q):[q],!0});define=function(b,c,d){var g,h;"string"!==typeof b&&(d=c,c=b,b=null);H(c)||(d=c,c=null);!c&&G(d)&&(c=[],d.length&&(d.toString().replace(la,"").replace(ma,function(b,d){c.push(d)}),c=(1===d.length?["require"]:["require","exports","module"]).concat(c)));if(O){if(!(g=L))P&&"interactive"===P.readyState||U(document.getElementsByTagName("script"),function(b){if("interactive"===b.readyState)return P=b}),g=P;g&&(b||
(b=g.getAttribute("data-requiremodule")),h=F[g.getAttribute("data-requirecontext")])}(h?h.defQueue:S).push([b,c,d])};define.amd={jQuery:!0};h.exec=function(b){return eval(b)};h(r)}})(this);

View file

@ -0,0 +1,138 @@
/*
* classList.js: Cross-browser full element.classList implementation.
* 2011-06-15
*
* By Eli Grey, http://eligrey.com
* Public Domain.
* NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
*/
/*global self, document, DOMException */
/*! @source http://purl.eligrey.com/github/classList.js/blob/master/classList.js*/
if (typeof document !== "undefined" && !("classList" in document.createElement("a"))) {
(function (view) {
"use strict";
var
classListProp = "classList"
, protoProp = "prototype"
, elemCtrProto = (view.HTMLElement || view.Element)[protoProp]
, objCtr = Object
, strTrim = String[protoProp].trim || function () {
return this.replace(/^\s+|\s+$/g, "");
}
, arrIndexOf = Array[protoProp].indexOf || function (item) {
var
i = 0
, len = this.length
;
for (; i < len; i++) {
if (i in this && this[i] === item) {
return i;
}
}
return -1;
}
// Vendors: please allow content code to instantiate DOMExceptions
, DOMEx = function (type, message) {
this.name = type;
this.code = DOMException[type];
this.message = message;
}
, checkTokenAndGetIndex = function (classList, token) {
if (token === "") {
throw new DOMEx(
"SYNTAX_ERR"
, "An invalid or illegal string was specified"
);
}
if (/\s/.test(token)) {
throw new DOMEx(
"INVALID_CHARACTER_ERR"
, "String contains an invalid character"
);
}
return arrIndexOf.call(classList, token);
}
, ClassList = function (elem) {
var
trimmedClasses = strTrim.call(elem.className)
, classes = trimmedClasses ? trimmedClasses.split(/\s+/) : []
, i = 0
, len = classes.length
;
for (; i < len; i++) {
this.push(classes[i]);
}
this._updateClassName = function () {
elem.className = this.toString();
};
}
, classListProto = ClassList[protoProp] = []
, classListGetter = function () {
return new ClassList(this);
}
;
// Most DOMException implementations don't allow calling DOMException's toString()
// on non-DOMExceptions. Error's toString() is sufficient here.
DOMEx[protoProp] = Error[protoProp];
classListProto.item = function (i) {
return this[i] || null;
};
classListProto.contains = function (token) {
token += "";
return checkTokenAndGetIndex(this, token) !== -1;
};
classListProto.add = function (token) {
token += "";
if (checkTokenAndGetIndex(this, token) === -1) {
this.push(token);
this._updateClassName();
}
};
classListProto.remove = function (token) {
token += "";
var index = checkTokenAndGetIndex(this, token);
if (index !== -1) {
this.splice(index, 1);
this._updateClassName();
}
};
classListProto.toggle = function (token) {
token += "";
if (checkTokenAndGetIndex(this, token) === -1) {
this.add(token);
} else {
this.remove(token);
}
};
classListProto.toString = function () {
return this.join(" ");
};
if (objCtr.defineProperty) {
var classListPropDesc = {
get: classListGetter
, enumerable: true
, configurable: true
};
try {
objCtr.defineProperty(elemCtrProto, classListProp, classListPropDesc);
} catch (ex) { // IE 8 doesn't support enumerable:true
if (ex.number === -0x7FF5EC54) {
classListPropDesc.enumerable = false;
objCtr.defineProperty(elemCtrProto, classListProp, classListPropDesc);
}
}
} else if (objCtr[protoProp].__defineGetter__) {
elemCtrProto.__defineGetter__(classListProp, classListGetter);
}
}(self));
}

View file

@ -0,0 +1,23 @@
if (!Function.prototype.bind) {
Function.prototype.bind = function (oThis) {
if (typeof this !== "function") {
// closest thing possible to the ECMAScript 5 internal IsCallable function
throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");
}
var aArgs = Array.prototype.slice.call(arguments, 1),
fToBind = this,
fNOP = function () {},
fBound = function () {
return fToBind.apply(this instanceof fNOP && oThis
? this
: oThis,
aArgs.concat(Array.prototype.slice.call(arguments)));
};
fNOP.prototype = this.prototype;
fBound.prototype = new fNOP();
return fBound;
};
}

View file

@ -0,0 +1,13 @@
// adds requestAnimationFrame functionality
// Source: http://strd6.com/2011/05/better-window-requestanimationframe-shim/
window.requestAnimationFrame || (window.requestAnimationFrame =
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function(callback, element) {
return window.setTimeout(function() {
callback(+new Date());
}, 1000 / 60);
});

View file

@ -0,0 +1,227 @@
Metamaps.Backbone = {};
Metamaps.Backbone.Map = Backbone.Model.extend({
urlRoot: '/maps',
blacklist: ['created_at', 'updated_at', 'created_at_clean', 'updated_at_clean', 'user_name', 'contributor_count', 'topic_count', 'synapse_count', 'topics', 'synapses', 'mappings', 'mappers'],
toJSON: function (options) {
return _.omit(this.attributes, this.blacklist);
},
save: function (key, val, options) {
var attrs;
// Handle both `"key", value` and `{key: value}` -style arguments.
if (key == null || typeof key === 'object') {
attrs = key;
options = val;
} else {
(attrs = {})[key] = val;
}
var newOptions = options || {};
var s = newOptions.success;
newOptions.success = function (model, response, opt) {
if (s) s(model, response, opt);
model.trigger('saved');
};
return Backbone.Model.prototype.save.call(this, attrs, newOptions);
},
initialize: function () {
this.on('changeByOther', this.updateView);
this.on('saved', this.savedEvent);
},
savedEvent: function() {
Metamaps.Realtime.sendMapChange(this);
},
authorizeToEdit: function (mapper) {
if (mapper && (this.get('permission') === "commons" || this.get('user_id') === mapper.get('id'))) return true;
else return false;
},
authorizePermissionChange: function (mapper) {
if (mapper && this.get('user_id') === mapper.get('id')) return true;
else return false;
},
getUser: function () {
return Metamaps.Mapper.get(this.get('user_id'));
},
fetchContained: function () {
var bb = Metamaps.Backbone;
var that = this;
var start = function (data) {
that.set('mappers', new bb.MapperCollection(data.mappers));
that.set('topics', new bb.TopicCollection(data.topics));
that.set('synapses', new bb.SynapseCollection(data.synapses));
that.set('mappings', new bb.MappingCollection(data.mappings));
};
var e = $.ajax({
url: "/maps/" + this.id + "/contains.json",
success: start,
error: errorFunc,
async: false
});
},
getTopics: function () {
if (!this.get('topics')) {
this.fetchContained();
}
return this.get('topics');
},
getSynapses: function () {
if (!this.get('synapses')) {
this.fetchContained();
}
return this.get('synapses');
},
getMappings: function () {
if (!this.get('mappings')) {
this.fetchContained();
}
return this.get('mappings');
},
getMappers: function () {
if (!this.get('mappers')) {
this.fetchContained();
}
return this.get('mappers');
},
attrForCards: function () {
function capitalize(string) {
return string.charAt(0).toUpperCase() + string.slice(1);
}
var n = this.get('name');
var d = this.get('desc');
var maxNameLength = 32;
var maxDescLength = 118;
var truncatedName = n ? (n.length > maxNameLength ? n.substring(0, maxNameLength) + "..." : n) : "";
var truncatedDesc = d ? (d.length > maxDescLength ? d.substring(0, maxDescLength) + "..." : d) : "";
var obj = {
id: this.id,
name: truncatedName,
fullName: n,
desc: truncatedDesc,
permission: this.get("permission") ? capitalize(this.get("permission")) : "Commons",
editPermission: this.authorizeToEdit(Metamaps.Active.Mapper) ? 'canEdit' : 'cannotEdit',
contributor_count_number: '<span class="cCountColor">' + this.get('contributor_count') + '</span>',
contributor_count_string: this.get('contributor_count') == 1 ? ' contributor' : ' contributors',
topic_count_number: '<span class="tCountColor">' + this.get('topic_count') + '</span>',
topic_count_string: this.get('topic_count') == 1 ? ' topic' : ' topics',
synapse_count_number: '<span class="sCountColor">' + this.get('synapse_count') + '</span>',
synapse_count_string: this.get('synapse_count') == 1 ? ' synapse' : ' synapses',
screenshot: '<img src="' + this.get('screenshot_url') + '" />'
};
return obj;
},
updateView: function() {
var map = Metamaps.Active.Map;
var isActiveMap = this.id === map.id;
var authorized = map && map.authorizeToEdit(Metamaps.Active.Mapper) ? 'canEditMap' : '';
var commonsMap = map && map.get('permission') === 'commons' ? 'commonsMap' : '';
if (isActiveMap) {
Metamaps.Map.InfoBox.updateNameDescPerm(this.get('name'), this.get('desc'), this.get('permission'));
this.updateMapWrapper();
}
},
updateMapWrapper: function() {
var map = Metamaps.Active.Map;
var isActiveMap = this.id === map.id;
var authorized = map && map.authorizeToEdit(Metamaps.Active.Mapper) ? 'canEditMap' : '';
var commonsMap = map && map.get('permission') === 'commons' ? 'commonsMap' : '';
if (isActiveMap) {
$('.wrapper').removeClass('canEditMap commonsMap').addClass(authorized + ' ' + commonsMap);
}
}
});
Metamaps.Backbone.MapsCollection = Backbone.Collection.extend({
model: Metamaps.Backbone.Map,
initialize: function(models, options) {
this.id = options.id;
this.sortBy = options.sortBy;
if (options.mapperId) {
this.mapperId = options.mapperId;
}
// this.page represents the NEXT page to fetch
this.page = models.length > 0 ? (models.length < 20 ? "loadedAll" : 2) : 1;
},
url: function() {
if (!this.mapperId) {
return '/explore/' + this.id + '.json';
}
else {
return '/explore/mapper/' + this.mapperId + '.json';
}
},
comparator: function (a, b) {
a = a.get(this.sortBy);
b = b.get(this.sortBy);
var temp;
if (this.sortBy === 'name') {
a = a ? a.toLowerCase() : "";
b = b ? b.toLowerCase() : "";
}
else {
// this is for updated_at and created_at
temp = a;
a = b;
b = temp;
a = (new Date(a)).getTime();
b = (new Date(b)).getTime();
}
return a > b ? 1 : a < b ? -1 : 0;
},
getMaps: function (cb) {
var self = this;
Metamaps.Loading.show();
if (this.page != "loadedAll") {
var numBefore = this.length;
this.fetch({
remove: false,
silent: true,
data: { page: this.page },
success: function (collection, response, options) {
// you can pass additional options to the event you trigger here as well
if (collection.length - numBefore < 20) {
self.page = "loadedAll";
}
else self.page += 1;
self.trigger('successOnFetch', cb);
},
error: function (collection, response, options) {
// you can pass additional options to the event you trigger here as well
self.trigger('errorOnFetch');
}
});
}
else {
self.trigger('successOnFetch', cb);
}
}
});
Metamaps.Backbone.Mapper = Backbone.Model.extend({
urlRoot: '/users',
blacklist: ['created_at', 'updated_at'],
toJSON: function (options) {
return _.omit(this.attributes, this.blacklist);
},
prepareLiForFilter: function () {
var li = '';
li += '<li data-id="' + this.id.toString() + '">';
li += '<img src="' + this.get("image") + '" data-id="' + this.id.toString() + '"';
li += ' alt="' + this.get('name') + '" />';
li += '<p>' + this.get('name') + '</p></li>';
return li;
}
});
Metamaps.Backbone.MapperCollection = Backbone.Collection.extend({
model: Metamaps.Backbone.Mapper,
url: '/users'
});

View file

@ -0,0 +1,661 @@
var Metamaps = {}; // this variable declaration defines a Javascript object that will contain all the variables and functions used by us, broken down into 'sub-modules' that look something like this
/*
* unless you are on a page with the Javascript InfoVis Toolkit (Topic or Map) the only section in the metamaps
* object will be these
GlobalUI
Active
Maps
Mappers
Backbone
* all these get added when you are on a page with the Javascript Infovis Toolkit
Settings
Touch
Mouse
Selected
Metacodes
Topics
Synapses
Mappings
Create
TopicCard
SynapseCard
Visualize
Util
Realtime
Control
Filter
Listeners
Organize
Map
Mapper
Topic
Synapse
JIT
*/
Metamaps.Active = {
Map: null,
Topic: null,
Mapper: null
};
Metamaps.Maps = {};
$(document).ready(function () {
for (var prop in Metamaps) {
// this runs the init function within each sub-object on the Metamaps one
if (Metamaps.hasOwnProperty(prop) &&
Metamaps[prop].hasOwnProperty('init') &&
typeof (Metamaps[prop].init) == 'function'
) {
Metamaps[prop].init();
}
}
// initialize the famous ui
var callFamous = function(){
if (Metamaps.Famous) {
Metamaps.Famous.build();
}
else {
setTimeout(callFamous, 100);
}
}
callFamous();
});
Metamaps.GlobalUI = {
notifyTimeout: null,
lightbox: null,
init: function () {
var self = Metamaps.GlobalUI;
self.Search.init();
self.CreateMap.init();
self.Account.init();
//bind lightbox clicks
$('.openLightbox').click(function (event) {
self.openLightbox($(this).attr('data-open'));
event.preventDefault();
return false;
});
$('#lightbox_screen, #lightbox_close').click(self.closeLightbox);
// initialize global backbone models and collections
if (Metamaps.Active.Mapper) Metamaps.Active.Mapper = new Metamaps.Backbone.Mapper(Metamaps.Active.Mapper);
var myCollection = Metamaps.Maps.Mine ? Metamaps.Maps.Mine : [];
var mapperCollection = [];
var mapperOptionsObj = {id: 'mapper', sortBy: 'updated_at' };
if (Metamaps.Maps.Mapper) {
mapperCollection = Metamaps.Maps.Mapper.models;
mapperOptionsObj.mapperId = Metamaps.Maps.Mapper.id;
}
var featuredCollection = Metamaps.Maps.Featured ? Metamaps.Maps.Featured : [];
var activeCollection = Metamaps.Maps.Active ? Metamaps.Maps.Active : [];
Metamaps.Maps.Mine = new Metamaps.Backbone.MapsCollection(myCollection, {id: 'mine', sortBy: 'updated_at' });
// 'Mapper' refers to another mapper
Metamaps.Maps.Mapper = new Metamaps.Backbone.MapsCollection(mapperCollection, mapperOptionsObj);
Metamaps.Maps.Featured = new Metamaps.Backbone.MapsCollection(featuredCollection, {id: 'featured', sortBy: 'updated_at' });
Metamaps.Maps.Active = new Metamaps.Backbone.MapsCollection(activeCollection, {id: 'active', sortBy: 'updated_at' });
},
openLightbox: function (which) {
var self = Metamaps.GlobalUI;
$('.lightboxContent').hide();
$('#' + which).show();
self.lightbox = which;
$('#lightbox_overlay').show();
var heightOfContent = '-' + ($('#lightbox_main').height() / 2) + 'px';
// animate the content in from the bottom
$('#lightbox_main').animate({
'top': '50%',
'margin-top': heightOfContent
}, 200, 'easeOutCubic');
// fade the black overlay in
$('#lightbox_screen').animate({
'opacity': '0.42'
}, 200);
if (which == "switchMetacodes") {
Metamaps.Create.isSwitchingSet = true;
}
},
closeLightbox: function (event) {
var self = Metamaps.GlobalUI;
if (event) event.preventDefault();
// animate the lightbox content offscreen
$('#lightbox_main').animate({
'top': '100%',
'margin-top': '0'
}, 200, 'easeInCubic');
// fade the black overlay out
$('#lightbox_screen').animate({
'opacity': '0.0'
}, 200, function () {
$('#lightbox_overlay').hide();
});
if (self.lightbox === 'forkmap') Metamaps.GlobalUI.CreateMap.reset('fork_map');
if (self.lightbox === 'newmap') Metamaps.GlobalUI.CreateMap.reset('new_map');
if (Metamaps.Create && Metamaps.Create.isSwitchingSet) {
Metamaps.Create.cancelMetacodeSetSwitch();
}
self.lightbox = null;
},
notifyUser: function (message, leaveOpen) {
var self = Metamaps.GlobalUI;
Metamaps.Famous.toast.surf.setContent(message);
Metamaps.Famous.toast.show();
clearTimeout(self.notifyTimeOut);
if (!leaveOpen) {
self.notifyTimeOut = setTimeout(function () {
Metamaps.Famous.toast.hide();
}, 8000);
}
},
clearNotify: function() {
var self = Metamaps.GlobalUI;
clearTimeout(self.notifyTimeOut);
Metamaps.Famous.toast.hide();
},
shareInvite: function(inviteLink) {
window.prompt("To copy the invite link, press: Ctrl+C, Enter", inviteLink);
}
};
Metamaps.GlobalUI.CreateMap = {
newMap: null,
emptyMapForm: "",
emptyForkMapForm: "",
topicsToMap: [],
synapsesToMap: [],
init: function () {
var self = Metamaps.GlobalUI.CreateMap;
self.newMap = new Metamaps.Backbone.Map({ permission: 'commons' });
self.bindFormEvents();
self.emptyMapForm = $('#new_map').html();
},
bindFormEvents: function () {
var self = Metamaps.GlobalUI.CreateMap;
$('.new_map button.cancel').unbind().bind('click', function (event) {
event.preventDefault();
Metamaps.GlobalUI.closeLightbox();
});
$('.new_map button.submitMap').unbind().bind('click', self.submit);
// bind permission changer events on the createMap form
$('.permIcon').unbind().bind('click', self.switchPermission);
},
closeSuccess: function () {
$('#mapCreatedSuccess').fadeOut(300, function(){
$(this).remove();
});
},
generateSuccessMessage: function (id) {
var stringStart = "<div id='mapCreatedSuccess'><h6>SUCCESS!</h6>Your map has been created. Do you want to: <a id='mapGo' href='/maps/";
stringStart += id;
stringStart += "' onclick='Metamaps.GlobalUI.CreateMap.closeSuccess();'>Go to your new map</a>";
stringStart += "<span>OR</span><a id='mapStay' href='#' onclick='Metamaps.GlobalUI.CreateMap.closeSuccess(); return false;'>Stay on this ";
var page = Metamaps.Active.Map ? 'map' : 'page';
var stringEnd = "</a></div>";
return stringStart + page + stringEnd;
},
switchPermission: function () {
var self = Metamaps.GlobalUI.CreateMap;
self.newMap.set('permission', $(this).attr('data-permission'));
$(this).siblings('.permIcon').find('.mapPermIcon').removeClass('selected');
$(this).find('.mapPermIcon').addClass('selected');
var permText = $(this).find('.tip').html();
$(this).parents('.new_map').find('.permText').html(permText);
},
submit: function (event) {
if (event) event.preventDefault();
var self = Metamaps.GlobalUI.CreateMap;
if (Metamaps.GlobalUI.lightbox === 'forkmap') {
self.newMap.set('topicsToMap', self.topicsToMap);
self.newMap.set('synapsesToMap', self.synapsesToMap);
}
var formId = Metamaps.GlobalUI.lightbox === 'forkmap' ? '#fork_map' : '#new_map';
var $form = $(formId);
self.newMap.set('name', $form.find('#map_name').val());
self.newMap.set('desc', $form.find('#map_desc').val());
if (self.newMap.get('name').length===0){
self.throwMapNameError();
return;
}
self.newMap.save(null, {
success: self.success
// TODO add error message
});
Metamaps.GlobalUI.closeLightbox();
Metamaps.GlobalUI.notifyUser('Working...');
},
throwMapNameError: function () {
var self = Metamaps.GlobalUI.CreateMap;
var formId = Metamaps.GlobalUI.lightbox === 'forkmap' ? '#fork_map' : '#new_map';
var $form = $(formId);
var message = $("<div class='feedback_message'>Please enter a map name...</div>");
$form.find('#map_name').after(message);
setTimeout(function(){
message.fadeOut('fast', function(){
message.remove();
});
}, 5000);
},
success: function (model) {
var self = Metamaps.GlobalUI.CreateMap;
//push the new map onto the collection of 'my maps'
Metamaps.Maps.Mine.add(model);
var formId = Metamaps.GlobalUI.lightbox === 'forkmap' ? '#fork_map' : '#new_map';
var form = $(formId);
Metamaps.GlobalUI.clearNotify();
$('#wrapper').append(self.generateSuccessMessage(model.id));
},
reset: function (id) {
var self = Metamaps.GlobalUI.CreateMap;
var form = $('#' + id);
if (id === "fork_map") {
self.topicsToMap = [];
self.synapsesToMap = [];
form.html(self.emptyForkMapForm);
}
else {
form.html(self.emptyMapForm);
}
self.bindFormEvents();
self.newMap = new Metamaps.Backbone.Map({ permission: 'commons' });
return false;
},
};
Metamaps.GlobalUI.Account = {
isOpen: false,
changing: false,
init: function () {
var self = Metamaps.GlobalUI.Account;
$('.sidebarAccountIcon').click(self.toggleBox);
$('.sidebarAccountBox').click(function(event){
event.stopPropagation();
});
$('body').click(self.close);
},
toggleBox: function (event) {
var self = Metamaps.GlobalUI.Account;
if (self.isOpen) self.close();
else self.open();
event.stopPropagation();
},
open: function () {
var self = Metamaps.GlobalUI.Account;
Metamaps.Realtime.close();
Metamaps.Filter.close();
$('.sidebarAccountIcon .tooltipsUnder').addClass('hide');
if (!self.isOpen && !self.changing) {
self.changing = true;
$('.sidebarAccountBox').fadeIn(200, function () {
self.changing = false;
self.isOpen = true;
$('.sidebarAccountBox #user_email').focus();
});
}
},
close: function () {
var self = Metamaps.GlobalUI.Account;
$('.sidebarAccountIcon .tooltipsUnder').removeClass('hide');
if (!self.changing) {
self.changing = true;
$('.sidebarAccountBox #user_email').blur();
$('.sidebarAccountBox').fadeOut(200, function () {
self.changing = false;
self.isOpen = false;
});
}
}
};
Metamaps.GlobalUI.Search = {
locked: false,
isOpen: false,
timeOut: null,
changing: false,
optionsInitialized: false,
init: function () {
var self = Metamaps.GlobalUI.Search;
var loader = new CanvasLoader('searchLoading');
loader.setColor('#4fb5c0'); // default is '#000000'
loader.setDiameter(24); // default is 40
loader.setDensity(41); // default is 40
loader.setRange(0.9); // default is 1.3
loader.show(); // Hidden by default
// bind the hover events
$(".sidebarSearch").hover(function () {
self.open()
}, function () {
self.close(800, false)
});
$('.sidebarSearchIcon').click(function (e) {
$('.sidebarSearchField').focus();
});
$('.sidebarSearch').click(function (e) {
e.stopPropagation();
});
$('body').click(function (e) {
self.close(0, false);
});
// open if the search is closed and user hits ctrl+/
// close if they hit ESC
$('body').bind('keyup', function (e) {
switch (e.which) {
case 191:
if ((e.ctrlKey && !self.isOpen) || (e.ctrlKey && self.locked)) {
self.open(true); // true for focus
}
break;
case 27:
if (self.isOpen) {
self.close(0, true);
}
break;
default:
break; //console.log(e.which);
}
});
self.startTypeahead();
},
lock: function() {
var self = Metamaps.GlobalUI.Search;
self.locked = true;
},
unlock: function() {
var self = Metamaps.GlobalUI.Search;
self.locked = false;
},
open: function (focus) {
var self = Metamaps.GlobalUI.Search;
clearTimeout(self.timeOut);
if (!self.isOpen && !self.changing && !self.locked) {
self.changing = true;
$('.sidebarSearch .twitter-typeahead, .sidebarSearch .tt-hint, .sidebarSearchField').animate({
width: '400px'
}, 300, function () {
if (focus) $('.sidebarSearchField').focus();
$('.sidebarSearchField, .sidebarSearch .tt-hint').css({
padding: '7px 10px 3px 10px',
width: '380px'
});
self.changing = false;
self.isOpen = true;
});
}
},
close: function (closeAfter, bypass) {
var self = Metamaps.GlobalUI.Search;
self.timeOut = setTimeout(function () {
if (!self.locked && !self.changing && self.isOpen && (bypass || $('.sidebarSearchField').val() == '')) {
self.changing = true;
$('.sidebarSearchField, .sidebarSearch .tt-hint').css({
padding: '7px 0 3px 0',
width: '400px'
});
$('.sidebarSearch .twitter-typeahead, .sidebarSearch .tt-hint, .sidebarSearchField').animate({
width: '0'
}, 300, function () {
$('.sidebarSearchField').typeahead('setQuery', '');
$('.sidebarSearchField').blur();
self.changing = false;
self.isOpen = false;
});
}
}, closeAfter);
},
startTypeahead: function () {
var self = Metamaps.GlobalUI.Search;
var mapheader = Metamaps.Active.Mapper ? '<div class="searchMapsHeader searchHeader"><h3 class="search-heading">Maps</h3><input type="checkbox" class="limitToMe" id="limitMapsToMe"></input><label for="limitMapsToMe" class="limitToMeLabel">added by me</label><div class="minimizeResults minimizeMapResults"></div><div class="clearfloat"></div></div>' : '<div class="searchMapsHeader searchHeader"><h3 class="search-heading">Maps</h3><div class="minimizeResults minimizeMapResults"></div><div class="clearfloat"></div></div>';
var topicheader = Metamaps.Active.Mapper ? '<div class="searchTopicsHeader searchHeader"><h3 class="search-heading">Topics</h3><input type="checkbox" class="limitToMe" id="limitTopicsToMe"></input><label for="limitTopicsToMe" class="limitToMeLabel">added by me</label><div class="minimizeResults minimizeTopicResults"></div><div class="clearfloat"></div></div>' : '<div class="searchTopicsHeader searchHeader"><h3 class="search-heading">Topics</h3><div class="minimizeResults minimizeTopicResults"></div><div class="clearfloat"></div></div>';
var mapperheader = '<div class="searchMappersHeader searchHeader"><h3 class="search-heading">Mappers</h3><div class="minimizeResults minimizeMapperResults"></div><div class="clearfloat"></div></div>';
var topics = {
name: 'topics',
limit: 9999,
dupChecker: function (datum1, datum2) {
return false;
},
template: $('#topicSearchTemplate').html(),
remote: {
url: '/search/topics?term=%QUERY',
replace: function () {
var q = '/search/topics?term=' + $('.sidebarSearchField').val();
if (Metamaps.Active.Mapper && $("#limitTopicsToMe").is(':checked')) {
q += "&user=" + Metamaps.Active.Mapper.id.toString();
}
return q;
},
filter: function (dataset) {
if (dataset.length == 0) {
dataset.push({
value: "No results",
label: "No results",
typeImageURL: "/assets/icons/wildcard.png",
rtype: "noresult"
});
}
return dataset;
}
},
engine: Hogan,
header: topicheader
};
var maps = {
name: 'maps',
limit: 9999,
dupChecker: function (datum1, datum2) {
return false;
},
template: $('#mapSearchTemplate').html(),
remote: {
url: '/search/maps?term=%QUERY',
replace: function () {
var q = '/search/maps?term=' + $('.sidebarSearchField').val();
if (Metamaps.Active.Mapper && $("#limitMapsToMe").is(':checked')) {
q += "&user=" + Metamaps.Active.Mapper.id.toString();
}
return q;
},
filter: function (dataset) {
if (dataset.length == 0) {
dataset.push({
value: "No results",
label: "No results",
rtype: "noresult"
});
}
return dataset;
}
},
engine: Hogan,
header: mapheader
};
var mappers = {
name: 'mappers',
limit: 9999,
dupChecker: function (datum1, datum2) {
return false;
},
template: $('#mapperSearchTemplate').html(),
remote: {
url: '/search/mappers?term=%QUERY',
filter: function (dataset) {
if (dataset.length == 0) {
dataset.push({
profile: "/assets/user.png",
value: "No results",
label: "No results",
rtype: "noresult"
});
}
return dataset;
}
},
engine: Hogan,
header: mapperheader
};
$('.sidebarSearchField').typeahead([topics, maps, mappers]);
//Set max height of the search results box to prevent it from covering bottom left footer
$('.sidebarSearchField').bind('typeahead:suggestionsRendered', function (event) {
self.initSearchOptions();
self.hideLoader();
var h = $(window).height();
$(".tt-dropdown-menu").css('max-height', h - 100);
});
$(window).resize(function () {
var h = $(window).height();
$(".tt-dropdown-menu").css('max-height', h - 100);
});
// tell the autocomplete to launch a new tab with the topic, map, or mapper you clicked on
$('.sidebarSearchField').bind('typeahead:selected', self.handleResultClick);
// don't do it, if they clicked on a 'addToMap' button
$('.sidebarSearch button.addToMap').click(function (event) {
event.stopPropagation();
});
// make sure that when you click on 'limit to me' or 'toggle section' it works
$('.sidebarSearchField').bind('typeahead:queryChanged', function(){
if ($(this).val() === "") {
self.hideLoader();
}
else {
self.showLoader();
}
});
},
handleResultClick: function (event, datum, dataset) {
var self = Metamaps.GlobalUI.Search;
self.hideLoader();
if (datum.rtype != "noresult") {
self.close(0, true);
var win;
if (dataset == "topics") {
Metamaps.Router.topics(datum.id);
} else if (dataset == "maps") {
Metamaps.Router.maps(datum.id);
} else if (dataset == "mappers") {
Metamaps.Router.explore("mapper", datum.id);
}
}
},
initSearchOptions: function () {
var self = Metamaps.GlobalUI.Search;
function toggleResultSet(set) {
var s = $('.tt-dataset-' + set + ' .tt-suggestions');
if (s.css('height') == '0px') {
s.css({
'height': 'auto',
'overflow': 'visible'
});
$(this).removeClass('maximizeResults').addClass('minimizeResults');
} else {
s.css({
'height': '0',
'overflow': 'hidden'
});
$(this).removeClass('minimizeResults').addClass('maximizeResults');
}
}
$('.limitToMe').unbind().bind("change", function (e) {
// set the value of the search equal to itself to retrigger the autocomplete event
self.isOpen = false;
$('.sidebarSearchField').typeahead('setQuery', $('.sidebarSearchField').val());
setTimeout(function () {
self.isOpen = true;
}, 2000);
});
// when the user clicks minimize section, hide the results for that section
$('.minimizeMapperResults').unbind().click(function (e) {
toggleResultSet.call(this, 'mappers');
});
$('.minimizeTopicResults').unbind().click(function (e) {
toggleResultSet.call(this, 'topics');
});
$('.minimizeMapResults').unbind().click(function (e) {
toggleResultSet.call(this, 'maps');
});
},
hideLoader: function () {
$('#searchLoading').hide();
},
showLoader: function () {
$('#searchLoading').show();
}
};

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,262 @@
(function () {
Metamaps.currentPage = "";
var Router = Backbone.Router.extend({
routes: {
"": "home", // #home
"explore/:section": "explore", // #explore/active
"explore/:section/:id": "explore", // #explore/mapper/1234
"maps/:id": "maps" // #maps/7
},
home: function () {
clearTimeout(Metamaps.routerTimeoutId);
if (Metamaps.Active.Mapper) document.title = 'Explore Active Maps | Metamaps';
else document.title = 'Home | Metamaps';
Metamaps.currentSection = "";
Metamaps.currentPage = "";
$('.wrapper').removeClass('mapPage topicPage');
var classes = Metamaps.Active.Mapper ? "homePage explorePage" : "homePage";
$('.wrapper').addClass(classes);
var navigate = function() {
Metamaps.routerTimeoutId = setTimeout(function() {
Metamaps.Router.navigate("");
}, 300);
};
// all this only for the logged in home page
if (Metamaps.Active.Mapper) {
Metamaps.Famous.yield.hide();
Metamaps.Famous.explore.set('active');
Metamaps.Famous.maps.resetScroll(); // sets the scroll back to the top
Metamaps.Famous.explore.show();
Metamaps.Famous.maps.show();
Metamaps.GlobalUI.Search.open();
Metamaps.GlobalUI.Search.lock();
Metamaps.Views.exploreMaps.setCollection( Metamaps.Maps.Active );
if (Metamaps.Maps.Active.length === 0) {
Metamaps.Maps.Active.getMaps(navigate); // this will trigger an explore maps render
}
else {
Metamaps.Views.exploreMaps.render(navigate);
}
}
// logged out home page
else {
Metamaps.Famous.yield.show();
Metamaps.Famous.explore.hide();
Metamaps.GlobalUI.Search.unlock();
Metamaps.GlobalUI.Search.close(0, true);
Metamaps.Famous.maps.hide();
Metamaps.routerTimeoutId = setTimeout(navigate, 500);
}
Metamaps.Famous.viz.hide();
Metamaps.Map.end();
Metamaps.Topic.end();
Metamaps.Active.Map = null;
Metamaps.Active.Topic = null;
},
explore: function (section, id) {
clearTimeout(Metamaps.routerTimeoutId);
// just capitalize the variable section
// either 'featured', 'mapper', or 'active'
var capitalize = section.charAt(0).toUpperCase() + section.slice(1);
if (section === "featured" || section === "active") {
document.title = 'Explore ' + capitalize + ' Maps | Metamaps';
}
else if (section === "mapper") {
$.ajax({
url: "/users/" + id + ".json",
success: function (response) {
document.title = response.name + ' | Metamaps';
},
error: function () {
}
});
}
else if (section === "mine") {
document.title = 'Explore My Maps | Metamaps';
}
$('.wrapper').removeClass('homePage mapPage topicPage');
$('.wrapper').addClass('explorePage');
Metamaps.currentSection = "explore";
Metamaps.currentPage = section;
// this will mean it's a mapper page being loaded
if (id) {
if (Metamaps.Maps.Mapper.mapperId !== id) {
// empty the collection if we are trying to load the maps
// collection of a different mapper than we had previously
Metamaps.Maps.Mapper.reset();
Metamaps.Maps.Mapper.page = 1;
}
Metamaps.Maps.Mapper.mapperId = id;
}
Metamaps.Views.exploreMaps.setCollection( Metamaps.Maps[capitalize] );
var navigate = function(){
var path = "/explore/" + Metamaps.currentPage;
// alter url if for mapper profile page
if (Metamaps.currentPage == "mapper") {
path += "/" + Metamaps.Maps.Mapper.mapperId;
}
Metamaps.Router.navigate(path);
};
var navigateTimeout = function() {
Metamaps.routerTimeoutId = setTimeout(navigate, 300);
};
if (Metamaps.Maps[capitalize].length === 0) {
Metamaps.Loading.show();
setTimeout(function(){
Metamaps.Maps[capitalize].getMaps(navigate); // this will trigger an explore maps render
}, 300); // wait 300 milliseconds till the other animations are done to do the fetch
}
else {
if (id) {
Metamaps.Views.exploreMaps.fetchUserThenRender(navigateTimeout);
}
else {
Metamaps.Views.exploreMaps.render(navigateTimeout);
}
}
Metamaps.GlobalUI.Search.open();
Metamaps.GlobalUI.Search.lock();
Metamaps.Famous.yield.hide();
Metamaps.Famous.maps.resetScroll(); // sets the scroll back to the top
Metamaps.Famous.maps.show();
Metamaps.Famous.explore.set(section, id);
Metamaps.Famous.explore.show();
Metamaps.Famous.viz.hide();
Metamaps.Map.end();
Metamaps.Topic.end();
Metamaps.Active.Map = null;
Metamaps.Active.Topic = null;
},
maps: function (id) {
clearTimeout(Metamaps.routerTimeoutId);
document.title = 'Map ' + id + ' | Metamaps';
Metamaps.currentSection = "map";
Metamaps.currentPage = id;
$('.wrapper').removeClass('homePage explorePage topicPage');
$('.wrapper').addClass('mapPage');
// another class will be added to wrapper if you
// can edit this map '.canEditMap'
Metamaps.Famous.yield.hide();
Metamaps.Famous.maps.hide();
Metamaps.Famous.explore.hide();
// clear the visualization, if there was one, before showing its div again
if (Metamaps.Visualize.mGraph) {
Metamaps.Visualize.mGraph.graph.empty();
Metamaps.Visualize.mGraph.plot();
Metamaps.JIT.centerMap(Metamaps.Visualize.mGraph.canvas);
}
Metamaps.Famous.viz.show();
Metamaps.Topic.end();
Metamaps.Active.Topic = null;
Metamaps.GlobalUI.Search.unlock();
Metamaps.GlobalUI.Search.close(0, true);
Metamaps.Loading.show();
Metamaps.Map.end();
Metamaps.Map.launch(id);
},
topics: function (id) {
clearTimeout(Metamaps.routerTimeoutId);
document.title = 'Topic ' + id + ' | Metamaps';
Metamaps.currentSection = "topic";
Metamaps.currentPage = id;
$('.wrapper').removeClass('homePage explorePage mapPage');
$('.wrapper').addClass('topicPage');
Metamaps.Famous.yield.hide();
Metamaps.Famous.maps.hide();
Metamaps.Famous.explore.hide();
// clear the visualization, if there was one, before showing its div again
if (Metamaps.Visualize.mGraph) {
Metamaps.Visualize.mGraph.graph.empty();
Metamaps.Visualize.mGraph.plot();
Metamaps.JIT.centerMap(Metamaps.Visualize.mGraph.canvas);
}
Metamaps.Famous.viz.show();
Metamaps.Map.end();
Metamaps.Active.Map = null;
Metamaps.GlobalUI.Search.unlock();
Metamaps.GlobalUI.Search.close(0, true);
Metamaps.Topic.end();
Metamaps.Topic.launch(id);
}
});
Metamaps.Router = new Router();
Metamaps.Router.intercept = function (evt) {
var segments;
var href = {
prop: $(this).prop("href"),
attr: $(this).attr("href")
};
var root = location.protocol + "//" + location.host + Backbone.history.options.root;
if (href.prop && href.prop === root) href.attr = "";
if (href.prop && href.prop.slice(0, root.length) === root) {
evt.preventDefault();
segments = href.attr.split('/');
segments.splice(0,1); // pop off the element created by the first /
if (href.attr === "") Metamaps.Router.home();
else {
Metamaps.Router[segments[0]](segments[1], segments[2]);
}
}
};
Metamaps.Router.init = function () {
Backbone.history.start({
silent: true,
pushState: true,
root: '/'
});
$(document).on("click", "a:not([data-bypass])", Metamaps.Router.intercept);
};
})();

View file

@ -0,0 +1,133 @@
(function () {
Metamaps.Views = {};
var initialized = false;
Metamaps.Views.init = function () {
Metamaps.Views.MapperCard = Backbone.View.extend({
template: Hogan.compile( $('#mapperCardTemplate').html() ),
tagNamea: "div",
className: "mapper",
render: function () {
this.$el.html( this.template.render(this.model) );
return this;
}
});
Metamaps.Views.MapCard = Backbone.View.extend({
template: Hogan.compile( $('#mapCardTemplate').html() ),
tagName: "div",
className: "map",
id: function() {
return this.model.id;
},
initialize: function () {
this.listenTo(this.model, "change", this.render);
},
render: function () {
this.$el.html( this.template.render(this.model.attrForCards()) );
return this;
}
});
var mapsWrapper = Backbone.View.extend({
initialize: function (opts) {
},
setCollection: function (collection) {
if (this.collection) this.stopListening(this.collection);
this.collection = collection;
this.listenTo(this.collection, 'add', this.render);
this.listenTo(this.collection, 'successOnFetch', this.handleSuccess);
this.listenTo(this.collection, 'errorOnFetch', this.handleError);
},
render: function (mapperObj, cb) {
var that = this;
if (typeof mapperObj === "function") {
var cb = mapperObj;
mapperObj = null;
}
this.el.innerHTML = "";
// in case it is a page where we have to display the mapper card
if (mapperObj) {
var view = new Metamaps.Views.MapperCard({ model: mapperObj });
that.el.appendChild( view.render().el );
}
this.collection.each(function (map) {
var view = new Metamaps.Views.MapCard({ model: map });
that.el.appendChild( view.render().el );
});
this.$el.append('<div class="clearfloat"></div>');
var m = Metamaps.Famous.maps.surf;
m.setContent(this.el);
var updateHeight = function(){
var height = $(that.el).height() + 32 + 56;
m.setSize([undefined, height]);
Metamaps.Famous.maps.lock = false;
if (cb) cb();
};
if (!initialized) {
m.deploy(m._currTarget);
initialized = true;
setTimeout(updateHeight, 100);
} else {
setTimeout(updateHeight, 100);
}
Metamaps.Loading.hide();
},
handleSuccess: function (cb) {
var that = this;
if (this.collection && this.collection.id === "mapper") {
this.fetchUserThenRender(cb);
}
else {
this.render(cb);
}
},
handleError: function () {
console.log('error loading maps!'); //TODO
},
fetchUserThenRender: function (cb) {
var that = this;
// first load the mapper object and then call the render function
$.ajax({
url: "/users/" + this.collection.mapperId + "/details.json",
success: function (response) {
that.render(response, cb);
},
error: function () {
that.render(cb);
}
});
}
});
Metamaps.Views.exploreMaps = new mapsWrapper();
};
})();

File diff suppressed because it is too large Load diff

View file

@ -1,12 +0,0 @@
/*
* This is a manifest file that'll be compiled into application.css, which will include all the files
* listed below.
*
* Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
* or vendor/assets/stylesheets of plugins, if any, can be referenced here using a relative path.
*
* You're free to add application-wide styles to this file and they'll appear at the top of the
* compiled file, but it's generally better to create a new file per style scope.
*
*= require ./special
*/

View file

@ -1,109 +0,0 @@
#metacodeSelector {
display: none;
}
.metacodeSelect {
border-top: 1px solid #DDD;
padding: 0;
background: #FFF;
.metacodeFilterInput {
width: 100px;
outline: none;
border: 0;
padding: 8px;
font-size: 14px;
line-height: 14px;
color: #424242;
font-family: 'din-medium', helvetica, sans-serif;
}
.metacodeList {
list-style: none;
background: #FFF;
li {
padding: 8px;
cursor: pointer;
&:hover, &.keySelect {
background: #4CAF50;
}
}
img {
width: 24px;
height: 24px;
display: inline-block;
vertical-align: middle;
padding-right: 6px;
}
}
}
.selectedMetacode {
float: left;
background: #FFF;
border-top-left-radius: 2px;
border-bottom-left-radius: 2px;
padding: 5px 10px 5px 6px;
vertical-align: top;
border-right: 1px solid #DDD;
cursor: pointer;
}
.selectedMetacode:hover, .selectedMetacode.isBeingSelected {
background: #EDEDED;
}
.selectedMetacode img {
width: 24px;
height: 24px;
display: inline-block;
vertical-align: middle;
}
.selectedMetacode span {
vertical-align: middle;
}
.selectedMetacode .downArrow {
display: inline-block;
vertical-align: middle;
width: 0;
height: 0;
border-style: solid;
border-width: 8px 6px 0 6px;
border-color: #777 transparent transparent transparent;
margin-left: 2px;
margin-top: 4px;
}
.new_topic {
margin: 0;
margin-top: -17px;
white-space: nowrap;
}
#new_topic .twitter-typeahead {
position: relative !important;
top: 0;
left: 0;
}
.new_topic #topic_name,
.new_topic .tt-hint {
border-radius: none;
border-top-right-radius: 2px;
border-bottom-right-radius: 2px;
}
.openMetacodeSwitcher {
top: -16px;
left: -16px;
}
#metacodeImg {
height: 120px;
width: 380px;
display: none;
position: absolute !important;
top: -30px;
z-index: -1;
}
#metacodeImgTitle {
display: none;
float: left;
width: 120px;
text-align: center;
margin-left: 110px;
}

View file

@ -0,0 +1,63 @@
.allMetacodes {
float:left;
}
.allMetacodes span {
margin:4px 8px;
color:#67AF9F;
}
.editMetacodes {
z-index:12;
width:auto;
color: #67AF9F;
padding:10px;
float:left;
}
.editMetacodes ul {
display:block;
}
.editMetacodes ul li {
clear:both;
list-style-type:none;
display:block;
padding:3px;
}
.editMetacodes ul img {
width:40px;
height:40px;
float:left;
}
.editMetacodes ul p {
float:left;
display: block;
margin: 0;
background: none;
padding: 10px 4px 2px 4px;
}
.editMetacodes #filters-one {
float:left;
}
.editMetacodes #filters-two {
float:left;
}
.editMetacodes #filters-three {
float:left;
}
.editMetacodes #filters-four {
float:left;
}
.editMetacodes li.toggledOff {
opacity: 0.4;
}

View file

@ -1,187 +0,0 @@
.allMetacodes {
float:left;
span {
margin:4px 8px;
color:#67AF9F;
}
}
.editMetacodes {
z-index:12;
width:auto;
color: #67AF9F;
padding:10px;
float:left;
ul {
display:block;
}
ul li {
clear:both;
list-style-type:none;
display:block;
padding:3px;
}
ul img {
width:40px;
height:40px;
float:left;
}
ul p {
float:left;
display: block;
margin: 0;
background: none;
padding: 10px 4px 2px 4px;
}
#filters-one {
float:left;
}
#filters-two {
float:left;
}
#filters-three {
float:left;
}
#filters-four {
float:left;
}
li.toggledOff {
opacity: 0.6;
}
}
.centerContent {
width: 760px;
margin: 0 auto;
padding: 80px 0 60px 20px;
background: rgba(125, 125, 125, 0.4);
overflow: hidden;
position: relative;
.metacodeSetsDescription {
width: 314px;
}
td.metacodeSetDesc {
width: 314px;
word-wrap: break-word;
}
.metacodeSetImage {
width: 36px;
height: 36px;
float: left;
}
tr {
display: table-row;
}
tr:nth-child(odd) {
background: rgba(125, 125, 125, 0.2);
}
tr:nth-child(even) {
background: rgba(125, 125, 125, 0.3);
}
th,
td {
padding: 10px;
}
td.iconURL {
max-width: 415px;
word-wrap: break-word;
}
.field {
margin: 15px 0 5px;
}
label {
float: left;
width: 100px;
margin-right: 15px;
margin-top: 0px;
}
input[type="text"] {
width: 336px;
height: 32px;
font-size: 15px;
direction: ltr;
-webkit-appearance: none;
appearance: none;
display: inline-block;
margin: 0;
padding: 0 8px;
background: #fff;
border: 1px solid #d9d9d9;
border-top: 1px solid #c0c0c0;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
-webkit-border-radius: 1px;
-moz-border-radius: 1px;
border-radius: 1px;
font: -webkit-small-control;
color: initial;
letter-spacing: normal;
word-spacing: normal;
text-transform: none;
text-indent: 0px;
text-shadow: none;
display: inline-block;
text-align: start;
font-family: arial;
}
textarea:hover,
input[type="text"]:hover {
border: 1px solid #b9b9b9;
border-top: 1px solid #a0a0a0;
-webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);
-moz-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);
box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);
}
textarea {
padding: 8px;
border: 1px solid #d9d9d9;
border-top: 1px solid #c0c0c0;
resize: none;
font: -webkit-small-control;
letter-spacing: normal;
word-spacing: normal;
text-transform: none;
text-indent: 0px;
text-shadow: none;
text-align: start;
font-family: arial;
font-size: 15px;
line-height: 17px;
width: 318px;
}
.allMetacodes {
padding: 5px 0;
}
a.button {
margin-right: 20px;
line-height: 40px;
}
a.button,
input.add {
float: left;
margin-top: 5px;
height: 40px;
font-size: 17px;
width: auto;
padding: 0 30px;
cursor: pointer;
font-weight: normal;
}
a.button:hover,
input.add:hover {
-webkit-box-shadow: none;
box-shadow: none;
}
}

Some files were not shown because too many files have changed in this diff Show more