This commit is contained in:
alex bisceglie 2011-03-24 13:09:20 -07:00
Родитель 9883965e45
Коммит 8adb8e1fcc
113 изменённых файлов: 30428 добавлений и 0 удалений

8
.gitignore поставляемый Normal file
Просмотреть файл

@ -0,0 +1,8 @@
settings_local.py
*.py[co]
*.sw[po]
*.pot
*.mo
pip-log.txt
build.py
.DS_Store

54
README.md Normal file
Просмотреть файл

@ -0,0 +1,54 @@
Markup
===
Firefox MarkUp is a [Django][Django]-based web application...
[Django]: http://www.djangoproject.com/
Getting Started
---
### Python
You need Python 2.6. Also, you probably want to run this application in a
[virtualenv][virtualenv] environment
[virtualenv]: http://pypi.python.org/pypi/virtualenv
### Dependencies
run
easy_install pip
followed by
./bootsrap.sh
### Django
South is used for migrations, so to sync the db, run
manage.py syncdb
and to run the migs
manage.py migrate
Then run the invite-generation script. generate_invites.py takes two vars: number of invites to generate, and type of invites to generate. invite-types are 't' for translator, or 'c' for contributor.
for 5 translator invites, run
manage.py generate_invites 5 t
for 10 contributor invites, run
manage.py generate_invites 10 c
For production environments, uncomment the following in settings:
# CACHE_BACKEND = 'caching.backends.memcached://localhost:11211?timeout=500'
# SESSION_COOKIE_SECURE = True
# SESSION_COOKIE_HTTPONLY = True
and set
REDIRECT_TO_SSL = True

Просмотреть файл

82
ffdemo/markup/common.py Normal file
Просмотреть файл

@ -0,0 +1,82 @@
from ffdemo.markup.models import Mark
from ffdemo.markup.models import Invitation
from ffdemo.utils import short_url
from datetime import datetime
import re
def get_invite_from_code(c):
invite = None
try:
invite = Invitation.objects.get(invite_code=c)
except Invitation.DoesNotExist:
return None
return invite
def get_translated_marks():
return
# convenience method to unpack marks for request return
def decode_mark_objects(data):
if data:
all_marks = []
for m in data:
# We need to decode the points obj simplified
decoded_points_obj = decode_points_obj(m.points_obj_simplified)
# Append to all marks
all_marks.append({'date_drawn': m.date_drawn.strftime("%a, %d %b %Y %I:%M:%S"), 'reference': m.reference, 'id': m.id, 'points_obj_simplified': decoded_points_obj, 'country_code': m.country_code, 'contributor': m.contributor, 'is_approved': m.is_approved})
return all_marks
else:
return
return
def save_new_mark_with_data(data):
# Remove whitespace from raw full points obj
stripped_points_obj_full = re.sub(r'\s', '', data['points_obj'])
# remove whitespace where not in extra_info (the contributor quote)
j = re.compile('^.*\"extra\_info"\:\"')
k = re.compile('\"extra\_info"\:\".*\"\,*.*$')
sec1 = j.search(data['points_obj_simplified'])
sec2 = k.search(data['points_obj_simplified'])
if sec1 and sec2:
stripped_sec1 = re.sub(r'\s','',sec1.group())
stripped_sec2 = re.sub('"extra_info":"','',sec2.group())
stripped_points_obj_simplified = stripped_sec1 + stripped_sec2
else:
stripped_points_obj_simplified = re.sub(r'\s','',data['points_obj_simplified'])
# stripped_points_obj_simplified = re.sub(r'\s', '', data['points_obj_simplified'])
# Encode both
encoded_points_obj_full = stripped_points_obj_full.encode('base64', 'strict')
encoded_points_obj_simplified = stripped_points_obj_simplified.encode('base64', 'strict')
# New mark
new_mark = Mark.objects.create()
new_mark.points_obj = encoded_points_obj_full
new_mark.points_obj_simplified = encoded_points_obj_simplified
new_mark.reference = short_url.encode_url(new_mark.id)
if 'country_code' in data:
new_mark.country_code = data['country_code']
invite = None
if 'invite' in data:
invite = get_invite_from_code(data['invite'])
if invite and 'contributor_locale' in data and len(data['contributor_locale'])>0:
new_mark.contributor_locale = data['contributor_locale']
else:
pass
if invite and 'contributor' in data and len(data['contributor'])>0:
new_mark.contributor = data['contributor']
else:
pass
else:
pass
new_mark.save()
if invite:
invite.used_at = datetime.now()
invite.save()
# Catch errors
return new_mark.reference
def decode_points_obj(obj):
returned_str = str(obj)
decoded_data = returned_str.decode('base64', 'strict')
return decoded_data

12
ffdemo/markup/forms.py Normal file
Просмотреть файл

@ -0,0 +1,12 @@
from django.forms import ModelForm
from ffdemo.markup.models import Mark
class MarkForm(ModelForm):
class Meta:
model = Mark
fields = ('points_obj', 'country_code')
class LoginForm(ModelForm):
class Meta:
model = Mark

Просмотреть файл

Просмотреть файл

Просмотреть файл

@ -0,0 +1,25 @@
import os, sys
from django.db import models
from django.core.management.base import BaseCommand, CommandError
from django.conf import settings
from ffdemo.markup.models import Invitation
class Command(BaseCommand):
args = '<num_invites invite_type>'
def handle(self, *args, **options):
if len(args) < 2:
raise CommandError('Requires number of invites to generate and type of invite')
num_invites = int(args[0])
invite_type = args[1]
valid_values = []
for choice_id, choice_label in settings.CONTRIBUTOR_TYPE_CHOICES:
valid_values += choice_id
if invite_type not in valid_values:
raise CommandError('Invite type must be in ', valid_values)
else:
print "generating ", num_invites, " ", invite_type, "invites"
for i in range(num_invites):
invite = Invitation(contributor_type=invite_type)
invite.save()
print "finished generating invites"

Просмотреть файл

@ -0,0 +1,65 @@
# encoding: utf-8
import datetime
from south.db import db
from south.v2 import SchemaMigration
from django.db import models
class Migration(SchemaMigration):
def forwards(self, orm):
# Adding model 'Mark'
db.create_table('markup_mark', (
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
('date_drawn', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, blank=True)),
('reference', self.gf('django.db.models.fields.CharField')(max_length=50, blank=True, unique=True)),
('points_obj', self.gf('django.db.models.fields.TextField')(blank=True)),
('points_obj_simplified', self.gf('django.db.models.fields.TextField')(blank=True)),
('country_code', self.gf('django.db.models.fields.CharField')(max_length=2, blank=True)),
('contributor_locale', self.gf('django.db.models.fields.CharField')(max_length=5, null=True, blank=True)),
('contributor', self.gf('django.db.models.fields.CharField')(max_length=75, null=True, blank=True)),
('flaggings', self.gf('django.db.models.fields.IntegerField')(default=0)),
('is_approved', self.gf('django.db.models.fields.BooleanField')(default=False))
))
db.send_create_signal('markup', ['Mark'])
# Adding model 'Invitation'
db.create_table('markup_invitation', (
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
('invite_code', self.gf('django.db.models.fields.SlugField')(db_index=True, max_length=50, blank=True)),
('contributor_type', self.gf('django.db.models.fields.CharField')(max_length=1)),
('used_at', self.gf('django.db.models.fields.DateTimeField')(blank=True))
))
db.send_create_signal('markup', ['Invitation'])
def backwards(self, orm):
# Deleting model 'Mark'
db.delete_table('markup_mark')
# Deleting model 'Invitation'
db.delete_table('markup_invitation')
models = {
'markup.invitation': {
'Meta': {'object_name': 'Invitation'},
'contributor_type': ('django.db.models.fields.CharField', [], {'max_length': '1'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'invite_code': ('django.db.models.fields.SlugField', [], {'max_length': '50', 'db_index': 'True'}),
'used_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'})
},
'markup.mark': {
'Meta': {'object_name': 'Mark'},
'contributor': ('django.db.models.fields.CharField', [], {'max_length': '75', 'null': 'True', 'blank': 'True'}),
'contributor_locale': ('django.db.models.fields.CharField', [], {'max_length': '5', 'null': 'True', 'blank': 'True'}),
'country_code': ('django.db.models.fields.CharField', [], {'max_length': '2', 'blank': 'True'}),
'date_drawn': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'flaggings': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_approved': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'points_obj': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'points_obj_simplified': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'reference': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'})
}
}
complete_apps = ['markup']

Просмотреть файл

@ -0,0 +1,50 @@
# encoding: utf-8
import datetime
from south.db import db
from south.v2 import SchemaMigration
from django.db import models
class Migration(SchemaMigration):
def forwards(self, orm):
# Adding unique constraint on 'Invitation', fields ['invite_code']
db.create_unique('markup_invitation', ['invite_code'])
# Adding index on 'Mark', fields ['reference']
db.create_index('markup_mark', ['reference'])
def backwards(self, orm):
# Removing index on 'Mark', fields ['reference']
db.delete_index('markup_mark', ['reference'])
# Removing unique constraint on 'Invitation', fields ['invite_code']
db.delete_unique('markup_invitation', ['invite_code'])
models = {
'markup.invitation': {
'Meta': {'object_name': 'Invitation'},
'contributor_type': ('django.db.models.fields.CharField', [], {'max_length': '1'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'invite_code': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '50', 'db_index': 'True'}),
'used_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'})
},
'markup.mark': {
'Meta': {'object_name': 'Mark'},
'contributor': ('django.db.models.fields.CharField', [], {'max_length': '75', 'null': 'True', 'blank': 'True'}),
'contributor_locale': ('django.db.models.fields.CharField', [], {'max_length': '5', 'null': 'True', 'blank': 'True'}),
'country_code': ('django.db.models.fields.CharField', [], {'max_length': '2', 'blank': 'True'}),
'date_drawn': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'flaggings': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_approved': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'points_obj': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'points_obj_simplified': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'reference': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'unique': 'True', 'max_length': '50', 'blank': 'True'})
}
}
complete_apps = ['markup']

Просмотреть файл

34
ffdemo/markup/models.py Normal file
Просмотреть файл

@ -0,0 +1,34 @@
from django.db import models
from django.template.defaultfilters import slugify
import hashlib
import uuid
from datetime import datetime
from django.conf import settings
class Mark(models.Model):
date_drawn = models.DateTimeField(auto_now_add=True)
reference = models.CharField(max_length=50, blank=True, db_index=True, unique=True)
points_obj = models.TextField(blank=True)
points_obj_simplified = models.TextField(blank=True)
country_code = models.CharField(max_length=2, blank=True)
flaggings = models.IntegerField(default=0)
is_approved = models.BooleanField(default=False)
# contributor attrs
contributor_locale = models.CharField(max_length=5, blank=True, null=True)
contributor = models.CharField(max_length=75, blank=True, null=True)
def __unicode__(self):
return unicode(self.date_drawn)
class Invitation(models.Model):
invite_code = models.SlugField(max_length=50, unique=True, db_index=True)
contributor_type = models.CharField(max_length=1, choices=settings.CONTRIBUTOR_TYPE_CHOICES)
used_at = models.DateTimeField(blank=True, null=True)
def save(self):
myuuid = uuid.uuid1().hex
self.invite_code = slugify( hashlib.md5(myuuid + datetime.now().strftime("%Y%m%d%H%m%s") ).hexdigest()[:12] )
super( Invitation, self ).save()
def __unicode__(self):
return self.invite_code

459
ffdemo/markup/requests.py Normal file
Просмотреть файл

@ -0,0 +1,459 @@
from django.http import HttpResponse
from ffdemo.markup.models import Mark
from ffdemo.markup import common
from django.utils import simplejson
from django.core import serializers
import datetime
from django.db.models import Q
def get_translated_marks(request):
marks_to_be_dumped = None
dthandler = lambda obj: obj.isoformat() if isinstance(obj, datetime.datetime) else None
response = {'success': False}
marks_to_be_dumped = Mark.objects.exclude(contributor_locale__isnull=True).order_by('id')
if marks_to_be_dumped:
all_marks = []
for m in marks_to_be_dumped:
# We need to decode the points obj simplified
decoded_points_obj = common.decode_points_obj(m.points_obj_simplified)
# Append to all marks
all_marks.append({'date_drawn': m.date_drawn.strftime("%a, %d %b %Y %I:%M:%S"), 'reference': m.reference, 'id': m.id, 'points_obj_simplified': decoded_points_obj, 'country_code': m.country_code, 'is_approved': m.is_approved, 'contributor_locale': m.contributor_locale})
response['success'] = True
response['marks'] = all_marks
else:
response['success'] = False
response['error'] = "No marks to be parsed"
json_response = simplejson.dumps(response)
return HttpResponse(json_response,'application/javascript')
def flag_mark(request):
response = {'success': False}
if 'reference' in request.POST and len(request.POST['reference']) > 0:
try:
mark = Mark.objects.get(reference=request.POST['reference'])
if mark.is_approved:
pass
else:
mark.flaggings += 1
mark.save()
response['success'] = True
except Mark.DoesNotExist:
response['error'] = "Mark does not exist"
except Mark.MultipleObjectsReturned:
# should never [ever] happen, purely CYA
response['error'] = "Multiple marks returned"
else:
response['error'] = "No mark specified"
json_response = simplejson.dumps(response)
return HttpResponse(json_response, 'application/javascript')
def init_viz_data(request):
# grab the last mark
# grab the first mark
response = {}
response['country_total_marks'] = ''
response['country_first_mark'] = ''
response['country_last_mark'] = ''
response['country_first_mark_at'] = ''
# for contributed marks
response['contributor_marks'] = []
contributor_marks = common.decode_mark_objects(Mark.objects.exclude(contributor__isnull=True).order_by('id'))
response['contributor_marks'] = contributor_marks
all_marks = Mark.objects.exclude(flaggings__gte=1).filter(contributor_locale__isnull=True).order_by('id')
if 'country_code' in request.GET and len(request.GET['country_code']) > 0:
country_marks = Mark.objects.exclude(flaggings__gte=1).filter(contributor_locale__isnull=True,country_code=request.GET['country_code']).order_by('id')
if len(country_marks) > 0:
response['country_total_marks'] = country_marks.count()
response['country_first_mark'] = country_marks[0].reference
response['country_last_mark'] = country_marks[response['country_total_marks']-1].reference
response['country_first_mark_at'] = country_marks[0].date_drawn.strftime("%a, %d %b %Y %I:%M:%S")
else:
pass
else:
pass
response['total_marks'] = all_marks.count()
response['last_mark'] = all_marks[response['total_marks']-1].reference
response['first_mark'] = all_marks[0].reference
response['first_mark_at'] = all_marks[0].date_drawn.strftime("%a, %d %b %Y %I:%M:%S")
response['total_countries'] = Mark.objects.values('country_code').distinct().count()
json_response = simplejson.dumps(response)
return HttpResponse(json_response, 'application/javascript')
def save_mark(request):
# Default response
response = {'success': False}
# Check for mandatory POST data
if 'points_obj' in request.POST and 'points_obj_simplified' in request.POST:
# Cosntruct mark data
mark_data = { 'points_obj': request.POST['points_obj'], 'points_obj_simplified': request.POST['points_obj_simplified'] }
if 'country_code' in request.POST:
mark_data['country_code'] = request.POST['country_code']
if 'invite' in request.POST:
mark_data['invite'] = request.POST['invite']
if 'contributor_locale' in request.POST:
mark_data['contributor_locale'] = request.POST['contributor_locale']
else:
pass
if 'contributor' in request.POST:
mark_data['contributor'] = request.POST['contributor']
else:
pass
else:
pass
# Save new mark, handled by common.py
new_mark_reference = common.save_new_mark_with_data(mark_data)
# Successful response, returning new mark reference
response['success'] = True
response['mark_reference'] = new_mark_reference
else:
# Error response
response['success'] = False
response['error'] = 'missing data in POST request'
# Return response as json
json_response = simplejson.dumps(response)
return HttpResponse(json_response, 'application/javascript')
def delete_mark(request):
# Completely remove the mark
response = {'success': False}
if 'reference' in request.POST and len(request.POST['reference']) > 0:
try:
m = Mark.objects.get(reference=request.POST['reference'])
m.delete()
response['success'] = True
except Mark.DoesNotExist:
response['error'] = 'Mark does not exist'
else:
response['error'] = "No mark specified"
json_response = simplejson.dumps(response)
return HttpResponse(json_response, 'application/javascript')
def approve_mark(request):
# Approve the mark // CHECK
response = {'success': False}
if 'reference' in request.POST and len(request.POST['reference']) > 0:
try:
m = Mark.objects.get(reference=request.POST['reference'])
should_approve = False
if request.POST['should_approve'] == "true":
should_approve = True
m.is_approved = should_approve
m.save()
response['success'] = True
except Mark.DoesNotExist:
response['error'] = 'Mark does not exist'
else:
response['error'] = "No mark specified"
json_response = simplejson.dumps(response)
return HttpResponse(json_response, 'application/javascript')
def get_mark(request):
# Get mark by ID
mark = None
try:
mark = Mark.objects.get(reference=request.GET['mark_id'])
except Mark.DoesNotExist:
pass
except Mark.MultipleObjectsReturned:
pass
previous_marks = ""
next_marks = ""
# Decode simplified points data
decoded_points_obj = common.decode_points_obj(mark.points_obj_simplified)
# Return raw
return HttpResponse(decoded_points_obj, 'application/javascript')
def marks_by_offset(request):
# Parameters:
# offset: Integer -
# max: Integer - (defaults 15)
# country_code: String - filter by country-code
#
# returns json object including relevant marks with their attributes: id, reference string, points_obj, points_obj_simplified
dthandler = lambda obj: obj.isoformat() if isinstance(obj, datetime.datetime) else None
response = {'success': False}
marks_to_be_dumped = None
did_fail_get_marks = False
# We've got an offset to play with
if 'offset' in request.GET:
# An offset requires a max value to be returned from this offset
if 'max' in request.GET:
offset = request.GET['offset']
max = request.GET['max']
# We can also filter by country code if need be
if 'country_code' in request.GET:
marks_to_be_dumped = Mark.objects.exclude.exclude(contributor_locale__isnull=False).filter(country_code=request.GET['country_code'])[offset:max]
else:
marks_to_be_dumped = Mark.objects.all()[offset:max]
else:
response['success'] = False
response['error'] = "Querying by offset also requires a 'max' POST var"
did_fail_get_marks = True
else:
# No special query parameters, query for all marks
marks_to_be_dumped = Mark.objects.exclude(contributor_locale__isnull=False)
# Check that we've got marks to dump
if not did_fail_get_marks:
if marks_to_be_dumped:
# Dump out
all_marks = []
for m in marks_to_be_dumped:
# We need to decode the points obj simplified
decoded_points_obj = common.decode_points_obj(m.points_obj_simplified)
# Append to all marks
all_marks.append({'date_drawn': m.date_drawn.strftime("%a, %d %b %Y %I:%M:%S"), 'reference': m.reference, 'points_obj_simplified': decoded_points_obj, 'contributor': m.contributor, 'country_code': m.country_code})
response['success'] = True
response['marks'] = all_marks
else:
# No marks to dump
response['success'] = False
response['error'] = "No marks to be parsed"
# Dump and return
json_response = simplejson.dumps(response, default=dthandler)
return HttpResponse(json_response, 'application/javascript')
def marks_by_locale(request):
# Parameters:
# country_code: String - filter by country-code
# max: Integer - (defaults 15)
#
# returns json object including relevant marks with their attributes: id, reference string, points_obj, points_obj_simplified
dthandler = lambda obj: obj.isoformat() if isinstance(obj, datetime.datetime) else None
response = {'success': False}
max_returned = 15
marks_to_be_dumped = None
did_fail_get_marks = False
def marks_by_reference(request):
# Parameters:
# reference_mark: String - slug of reference mark
# include_back: Integer - number of returned marks before the reference mark (defaults to 0)
# include_forward: Integer - number of returned marks after the reference mark (defaults to 15)
# include_mark: Boolean - include the reference mark (defaults true)
# country_code: String - filter by country-code
# returns json object including relevant marks with their attributes: id, reference string, points_obj, points_obj_simplified
dthandler = lambda obj: obj.isoformat() if isinstance(obj, datetime.datetime) else None
response = {'success': False}
reference_mark = None
include_back = 0
include_forward = 15
include_mark = True
country_code = None
marks_to_be_dumped = None
did_fail_get_marks = False
m_offset = None
# default limit returns 19 marks
m_limit = include_back + 1 + include_forward
all_marks = None
offset_index = 0
total_marks = 0
if 'reference' in request.GET:
reference_mark = request.GET['reference']
try:
m_offset = Mark.objects.get(reference=reference_mark)
except Mark.DoesNotExist:
response['success'] = False
response['error'] = "Reference mark doesn't exist"
did_fail_get_marks = True
except Mark.MultipleObjectsReturned:
response['success'] = False
response['error'] = "Multiple marks found for reference"
did_fail_get_marks = True
if 'include_mark' in request.GET:
if int(request.GET['include_mark']) == 0:
include_mark = False
if 'include_forward' in request.GET:
include_forward = int(request.GET['include_forward'])
if 'include_back' in request.GET:
include_back = int(request.GET['include_back'])
if 'country_code' in request.GET:
kountry_code = request.GET['country_code']
all_marks = Mark.objects.exclude(flaggings__gte=1).filter(country_code=kountry_code,contributor_locale__isnull=True).order_by('id')
total_marks = all_marks.count()
for i, item in enumerate(all_marks):
if item.reference == reference_mark:
offset_index = i
break
relative_include_back = offset_index - include_back
if relative_include_back < 0: relative_include_back = 0
unflagged_marks = Mark.objects.exclude(flaggings__gte=1).filter(contributor_locale__isnull=True)
if len(unflagged_marks) > 0:
marks_to_be_dumped = unflagged_marks.exclude(flaggings__gte=1,contributor_locale__isnull=False).filter(country_code=kountry_code,contributor_locale__isnull=True).order_by('id')[relative_include_back:offset_index+include_forward]
else:
response['success'] = False
response['error'] = "No marks to be dumped"
did_fail_get_marks = True
else:
all_marks = Mark.objects.exclude(flaggings__gte=1).filter(contributor_locale__isnull=True).order_by('id')
total_marks = all_marks.count()
for i, item in enumerate(all_marks):
if item.reference == reference_mark:
offset_index = i
break
relative_include_back = offset_index - include_back
if relative_include_back < 0: relative_include_back = 0
try:
marks_to_be_dumped = Mark.objects.exclude(flaggings__gte=1).filter(contributor_locale__isnull=True).order_by('id')[relative_include_back:offset_index+include_forward]
except Mark.DoesNotExist:
response['success'] = False
response['error'] = "No marks to be dumped"
did_fail_get_marks = True
else:
# required param
response['success'] = False
response['error'] = "Querying by reference requires a reference string"
did_fail_get_marks = True
# Check that we've got marks to dump
if not did_fail_get_marks:
if marks_to_be_dumped:
# Dump out
all_marks = []
for m in marks_to_be_dumped:
is_reference_mark = False
if m.reference == reference_mark:
is_reference_mark = True
if include_mark == False and is_reference_mark:
pass
else:
# We need to decode the points obj simplified
decoded_points_obj = common.decode_points_obj(m.points_obj_simplified)
# Append to all marks
all_marks.append({'is_reference_mark': is_reference_mark,'date_drawn': m.date_drawn.strftime("%a, %d %b %Y %I:%M:%S"), 'reference': m.reference, 'id': m.id, 'points_obj_simplified': decoded_points_obj, 'contributor': m.contributor, 'country_code': m.country_code})
response['success'] = True
response['marks'] = all_marks
else:
# No marks to dump
response['success'] = False
response['error'] = "No marks to be parsed"
# Dump and return
json_response = simplejson.dumps(response, default=dthandler)
return HttpResponse(json_response, 'application/javascript')
def all_marks(request):
# Get all marks, all data per mark excluding full points object
# This method can be queried via POST depending what the frontend requires
# Handler for dumping datetime field as JSON
#
# offset: Integer -
# max: Integer - (defaults 15)
# country_code: String - filter by country-code
#
# returns json object including relevant marks with their attributes: id, reference string, points_obj, points_obj_simplified
dthandler = lambda obj: obj.isoformat() if isinstance(obj, datetime.datetime) else None
response = {'success': False}
include_back = 3
include_forward = 15
include_mark = True
max_returned = 15
marks_to_be_dumped = None
did_fail_get_marks = False
# We've got an offset to play with
if 'offset' in request.GET:
# An offset requires a max value to be returned from this offset
if 'max' in request.GET:
offset = request.GET['offset']
max = request.GET['max']
# We can also filter by country code if need be
if 'country_code' in request.GET:
marks_to_be_dumped = Mark.objects.exclude(flaggings__gte=1).filter(contributor_locale__isnull=True).order_by('id').filter(country_code=request.GET['country_code'])[offset:max]
else:
marks_to_be_dumped = Mark.objects.exclude(flaggings__gte=1).filter(contributor_locale__isnull=True).order_by('id')[offset:max]
else:
response['success'] = False
response['error'] = "Querying by offset also requires a 'max' POST var"
did_fail_get_marks = True
else:
# We can also filter by country code here as well if need be
if 'country_code' in request.GET:
marks_to_be_dumped = Mark.objects.exclude(flaggings__gte=1).filter(contributor_locale__isnull=True,country_code=request.GET['country_code'])
else:
# No special query parameters, query for all marks
marks_to_be_dumped = Mark.objects.exclude(flaggings__gte=1).filter(contributor_locale__isnull=True)
# Check that we've got marks to dump
if not did_fail_get_marks:
if marks_to_be_dumped:
# Dump out
all_marks = []
for m in marks_to_be_dumped:
# We need to decode the points obj simplified
decoded_points_obj = common.decode_points_obj(m.points_obj_simplified)
# Append to all marks
all_marks.append({'date_drawn': m.date_drawn.strftime("%a, %d %b %Y %I:%M:%S"), 'reference': m.reference, 'id': m.id, 'points_obj_simplified': decoded_points_obj, 'contributor': m.contributor, 'country_code': m.country_code, 'flaggings': m.flaggings})
response['success'] = True
response['marks'] = all_marks
else:
# No marks to dump
response['success'] = False
response['error'] = "No marks to be parsed"
# Dump and return
json_response = simplejson.dumps(response, default=dthandler)
return HttpResponse(json_response, 'application/javascript')
def marks_by_flagged(request):
dthandler = lambda obj: obj.isoformat() if isinstance(obj, datetime.datetime) else None
response = {'success': False}
include_back = 3
include_forward = 15
include_mark = True
max_returned = 15
marks_to_be_dumped = Mark.objects.filter(flaggings__gte=1).order_by('id') # CHECK offset?
# Check that we've got marks to dump
if marks_to_be_dumped:
# Dump out
all_marks = []
for m in marks_to_be_dumped:
# We need to decode the points obj simplified
decoded_points_obj = common.decode_points_obj(m.points_obj_simplified)
# Append to all marks
all_marks.append({'date_drawn': m.date_drawn, 'reference': m.reference, 'id': m.id, 'points_obj_simplified': decoded_points_obj, 'contributor': m.contributor, 'country_code': m.country_code, 'is_approved': m.is_approved})
response['success'] = True
response['marks'] = all_marks
else:
# No marks to dump
response['success'] = False
response['error'] = "No marks to be parsed"
# Dump and return
json_response = simplejson.dumps(response, default=dthandler)
return HttpResponse(json_response, 'application/javascript')
def update_language(request):
response = {}
if 'language_code' in request.GET:
set_language(request)
else:
pass
return response

Просмотреть файл

Просмотреть файл

@ -0,0 +1,8 @@
from django import template
register = template.Library()
@register.filter(name='normalize_point')
def normalize_point(value,arg):
ret_val = float(value)/arg
return str(round(ret_val, 2)).replace(',','.')

119
ffdemo/markup/views.py Normal file
Просмотреть файл

@ -0,0 +1,119 @@
import json
import string
from django.http import Http404, HttpResponse, HttpResponseRedirect
from django.utils.encoding import force_unicode
from django.utils import translation
from django.utils.translation import ugettext_lazy as _lazy
from django.utils.translation import ugettext as _
from django.utils import simplejson
from django.conf import settings
from django.contrib.sites.models import Site
from django.template.loader import get_template
from django.contrib.auth.decorators import login_required
from django.contrib.auth import authenticate, login, logout
from django.views.decorators.cache import cache_page
from django.core.context_processors import csrf
from ffdemo.utils.render import render_response
from ffdemo.markup.models import Mark
from ffdemo.markup.forms import MarkForm
from django.shortcuts import get_object_or_404
from ffdemo.markup import common
@cache_page(15) # cache for 15 seconds
def home(request):
mr = settings.MEDIA_ROOT
return render_response(request, 'home.html', {'mr':mr})
@cache_page(60 * 30) # cache for 30 minutes
def about(request):
return render_response(request, 'about.html')
@cache_page(60 * 30)
def about_gml(request):
return render_response(request, 'gml.html')
@cache_page(60 * 30)
def credits(request):
return render_response(request, 'credits.html')
@cache_page(60 * 30)
def code(request):
return render_response(request, 'code.html')
@cache_page(60 * 30)
def mozilla(request):
return render_response(request, 'mozilla.html')
@cache_page(60 * 30)
def evan(request):
return render_response(request, 'evan-roth.html')
def gml(request):
mark = Mark.objects.all()[10]
obj_decoded = simplejson.loads(common.decode_points_obj(mark.points_obj_simplified))
context = { 'mark': mark, 'obj_decoded': obj_decoded }
return render_response(request, 'gml.xml', context, mimetype='application/xml')
@cache_page(60 * 30)
def manifesto(request):
return render_response(request, 'manifesto.html')
def mark(request, mark_reference):
mark = get_object_or_404(Mark, reference=mark_reference)
return render_response(request, 'mark.html', {'mark': mark})
def makemark(request):
if request.method == "POST":
mark_form = MarkForm(request.POST)
if mark_form.is_valid():
mark_data = { 'points_obj': mark_form.cleaned_data['points_obj'], 'country_code': mark_form.cleaned_data['country_code'] }
common.save_new_mark_with_data(mark_data)
else:
mark_form = MarkForm()
return render_response(request, 'makemark.html', {'form': mark_form})
@cache_page(30)
def community(request):
if 'offset' in request.GET:
offset = int(request.GET['offset'])
per_page = int(request.GET['per_page'])
top_limit = offset + per_page;
all_marks = Mark.objects.exclude(flaggings__gte=1)[offset:top_limit]
else:
all_marks = Mark.objects.exclude(flaggings__gte=1)
return render_response(request, 'community.html', {'all_marks': all_marks})
@cache_page(60 * 30)
def newsletter(request):
return render_response(request, 'newsletter.html', {})
@cache_page(60 * 30)
def home_sammy(request):
mr = settings.MEDIA_ROOT
return render_response(request,'sammy/home.html',{'mr':mr})
@cache_page(15)
def linear_sammy(request):
return render_response(request, 'sammy/linear.html')
def mark_sammy(request):
mark = get_object_or_404(Mark, reference=mark_reference)
return render_response(request, 'sammy/mark.html', {'mark':mark})
def makemark_sammy(request):
mark_form = MarkForm()
return render_response(request, 'sammy/makemark.html', {'form': mark_form})
### MODERATION VIEWS
def account_locked(request):
return render_response(request, 'registration/locked.html')
def moderate_sammy(request):
if not request.user.is_authenticated():
return HttpResponseRedirect('/accounts/login/')
else:
return render_response(request, 'sammy/moderate.html')

72
ffdemo/middleware.py Normal file
Просмотреть файл

@ -0,0 +1,72 @@
from django.db import connection
from django.conf import settings
from django.utils.encoding import *
from urlparse import urlparse
import re
from django import http
import django.core.exceptions
from django.core import urlresolvers
from django.http import HttpResponseRedirect
from django.utils import translation
import localeurl
from localeurl import utils
from django.http import HttpResponseRedirect, HttpResponsePermanentRedirect, get_host
class SQLLogMiddleware:
def process_response(self, request, response):
if settings.DEV and connection.queries:
time = sum([float(q['time']) for q in connection.queries])
return response
class DetectReferrer:
def process_request(self, request):
if not request.session.get('HTTP_REFERER', False):
if(request.META.get('HTTP_REFERER')):
ref = urlparse(request.META.get('HTTP_REFERER'))
request.session['HTTP_REFERER'] = ref.hostname
# SSL Middleware
# via: http://djangosnippets.org/snippets/85/
# __license__ = "Python"
# __copyright__ = "Copyright (C) 2007, Stephen Zabel"
# __author__ = "Stephen Zabel - sjzabel@gmail.com"
# __contributors__ = "Jay Parlar - parlar@gmail.com"
SSL = 'SSL'
class SSLRedirect:
def process_view(self, request, view_func, view_args, view_kwargs):
if SSL in view_kwargs:
secure = view_kwargs[SSL]
del view_kwargs[SSL]
else:
secure = False
if not secure == self._is_secure(request):
return self._redirect(request, secure)
def _is_secure(self, request):
if request.is_secure():
return True
#Handle the Webfaction case until this gets resolved in the request.is_secure()
if 'HTTP_X_FORWARDED_SSL' in request.META:
return request.META['HTTP_X_FORWARDED_SSL'] == 'on'
return False
def _redirect(self, request, secure):
protocol = secure and "https" or "http"
newurl = "%s://%s%s" % (protocol,get_host(request),request.get_full_path())
if settings.DEBUG and request.method == 'POST':
raise RuntimeError, \
"""Django can't perform a SSL redirect while maintaining POST data.
Please structure your views so that redirects only occur during GETs."""
return HttpResponsePermanentRedirect(newurl)

207
ffdemo/settings.py Normal file
Просмотреть файл

@ -0,0 +1,207 @@
import settings_local
import os
import re
# Django settings for ff4 project.
DEBUG = settings_local.DEBUG
DEV = settings_local.DEV
TEMPLATE_DEBUG = DEBUG
PROJECT_PATH = os.path.realpath(os.path.dirname(__file__))
PROJECT_DOMAIN = ''
PROJECT_DIR = os.path.realpath(os.path.dirname(__file__))
# UNCOMMENT TO ENABLE SECURE SESSIONS
# SESSION_COOKIE_SECURE = True
# SESSION_COOKIE_HTTPONLY = True
# SESSION_COOKIE_DOMAIN = None
# global for enabling SSL redirection of admin views
# set True for properly configured production
REDIRECT_TO_SSL = False
ADMINS = (
# ('Your Name', 'your_email@domain.com'),
)
MANAGERS = ADMINS
# Memcached!
# CACHE_BACKEND = 'caching.backends.memcached://localhost:11211?timeout=500'
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql', # Add 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'.
'NAME': settings_local.DB_NAME, # Or path to database file if using sqlite3.
'USER': settings_local.DB_USER, # Not used with sqlite3.
'PASSWORD': settings_local.DB_PASSWORD, # Not used with sqlite3.
'HOST': settings_local.DB_HOST, # Set to empty string for localhost. Not used with sqlite3.
'PORT': settings_local.DB_PORT, # Set to empty string for default. Not used with sqlite3.
}
}
# Local time zone for this installation. Choices can be found here:
# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name
# although not all choices may be available on all operating systems.
# On Unix systems, a value of None will cause Django to use the same
# timezone as the operating system.
# If running in a Windows environment this must be set to the same as your
# system time zone.
TIME_ZONE = 'America/New_York'
# Language code for this installation. All choices can be found here:
# http://www.i18nguy.com/unicode/language-identifiers.html
LANGUAGE_CODE = 'en-US'
SITE_ID = 1
# If you set this to False, Django will make some optimizations so as not
# to load the internationalization machinery.
USE_I18N = True
# If you set this to False, Django will not format dates, numbers and
# calendars according to the current locale
USE_L10N = True
# Accepted locales
#INPUT_LANGUAGES = ('ar', 'bg', 'ca', 'cs', 'da', 'de', 'el', 'en-US', 'es',
# 'fr', 'fy-NL', 'gl', 'he', 'hu', 'id', 'it', 'ko', 'nb-NO',
# 'nl', 'pl', 'pt-PT', 'ro', 'ru', 'sk', 'sq', 'uk', 'vi',
# 'zh-CN', 'zh-TW')
gettext = lambda s: s
LANGUAGES = (
('de', gettext('German')),
('en', gettext('English')),
('fr', gettext('French')),
('ru', gettext('Russian')),
)
# default to accept-language header, per localeurl's settings
LOCALEURL_USE_ACCEPT_LANGUAGE = True
# don't url-localize requests
LOCALE_INDEPENDENT_PATHS = (
re.compile('requests/'),
re.compile('/accounts/login/$'),
re.compile('/accounts/logout/$'),
re.compile('/i18n/'),
)
#RTL_LANGUAGES = ('ar', 'he',) # ('fa', 'fa-IR')
# Fallbacks for locales that are not recognized by Babel. Bug 596981.
BABEL_FALLBACK = {'fy-nl': 'nl'}
# Absolute path to the directory that holds media.
# Example: "/home/media/media.lawrence.com/"
MEDIA_ROOT = PROJECT_PATH+'/static/'
# URL that handles the media served from MEDIA_ROOT. Make sure to use a
# trailing slash if there is a path component (optional in other cases).
# Examples: "http://media.lawrence.com", "http://example.com/media/"
MEDIA_URL = '/media/'
# URL prefix for admin media -- CSS, JavaScript and images. Make sure to use a
# trailing slash.
# Examples: "http://foo.com/media/", "/media/".
ADMIN_MEDIA_PREFIX = '/media/admin/'
# Make this unique, and don't share it with anybody.
SECRET_KEY = 'priU+iaciut#uV&aphlADo#zlep?i!rlethiu-wOuslapr2eSp'
# List of callables that know how to import templates from various sources.
TEMPLATE_LOADERS = (
#'ffdemo.jinja.Loader',
'django.template.loaders.filesystem.Loader',
'django.template.loaders.app_directories.Loader',
# 'django.template.loaders.eggs.Loader',
)
MIDDLEWARE_CLASSES = (
'localeurl.middleware.LocaleURLMiddleware',
'django.middleware.common.CommonMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'axes.middleware.FailedLoginMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
#'django.middleware.locale.LocaleMiddleware',
'ffdemo.middleware.SQLLogMiddleware',
'ffdemo.middleware.SSLRedirect',
)
# ADMIN
CONTRIBUTOR_TYPE_CHOICES = (
('c','contributor'),
('t','translator'),
)
LOGIN_REDIRECT_URL = "/#/moderate"
# AXES SEC. CONFIG
AXES_LOGIN_FAILURE_LIMIT = 5
AXES_LOCK_OUT_AT_FAILURE = True
AXES_COOLOFF_TIME = 2
AXES_LOCKOUT_URL = "/auth/locked/"
ROOT_URLCONF = 'ffdemo.urls'
TEMPLATE_DIRS = (
PROJECT_PATH+'/templates_orig',
PROJECT_PATH+'/templates_orig/sammy',
)
# JINJA_TEMPLATE_DIRS = (
# PROJECT_PATH+'/templates',
# )
def JINJA_CONFIG():
import jinja2
config = {'extensions': ['jinja2.ext.loopcontrols',
'jinja2.ext.with_', 'caching.ext.cache'],
'finalize': lambda x: x if x is not None else ''}
return config
TEMPLATE_CONTEXT_PROCESSORS = (
'django.core.context_processors.request',
'django.core.context_processors.i18n',
'django.contrib.auth.context_processors.auth',
)
SERIALIZATION_MODULES = {
'yml': "django.core.serializers.pyyaml"
}
INSTALLED_APPS = (
'localeurl',
'ffdemo.markup',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.sites',
'django.contrib.messages',
'south',
'axes',
)
FIXTURE_DIRS = (
PROJECT_PATH+'/fixtures/',
)
SOUTH_TESTS_MIGRATE = False
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_HOST = settings_local.EMAIL_HOST
EMAIL_PORT = settings_local.EMAIL_PORT
EMAIL_HOST_USER = settings_local.EMAIL_HOST_USER
EMAIL_HOST_PASSWORD = settings_local.EMAIL_HOST_PASSWORD
EMAIL_USE_TLS = settings_local.EMAIL_USE_TLS

34
ffdemo/static/Jimfile Normal file
Просмотреть файл

@ -0,0 +1,34 @@
{
"vendor_dir": "assets/js/vendor",
"compressed_path": "assets/js/compressed.js",
"bundle_dir": "assets/js",
"bundles": {
"markApp": [
"jquery.markApp",
"jquery.markApp.linear",
"jquery.markApp.capture",
"jquery.markApp.intro",
"jquery.delayedBind",
"jquery.collapsibleMod",
"jquery.socialShare",
"jquery.ui-selectBox",
"json2",
"canvas_extensions",
"mark.layer",
"mark.layerManager",
"mark.gmlPoint",
"mark.simplifyPath",
"mark.brushes",
"mark.gmlMark",
"mark.scene",
"mark.renderer",
"mark.camera",
"mark.base",
"tween",
"sammy-0.6.3",
"sammy.hash_push_proxy",
"sammy.template"
]
},
"compressed_suffix": ".min"
}

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Двоичные данные
ffdemo/static/assets/images/arrow.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 192 B

Двоичные данные
ffdemo/static/assets/images/arrow_orange.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 372 B

Двоичные данные
ffdemo/static/assets/images/bg.jpg Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 6.8 KiB

Двоичные данные
ffdemo/static/assets/images/cancel.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 1.2 KiB

Двоичные данные
ffdemo/static/assets/images/collapsed.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 145 B

Двоичные данные
ffdemo/static/assets/images/collapsible.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 989 B

Двоичные данные
ffdemo/static/assets/images/coming-soon-tip-bg.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 1.1 KiB

Двоичные данные
ffdemo/static/assets/images/contributor_icon.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 1023 B

Двоичные данные
ffdemo/static/assets/images/contributor_label.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 1.0 KiB

Двоичные данные
ffdemo/static/assets/images/controls.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 1.6 KiB

Двоичные данные
ffdemo/static/assets/images/evan_roth.jpg Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 2.2 KiB

Двоичные данные
ffdemo/static/assets/images/evan_roth_bio.jpg Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 23 KiB

Двоичные данные
ffdemo/static/assets/images/facebook.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 2.1 KiB

Двоичные данные
ffdemo/static/assets/images/favicon.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 1.2 KiB

Двоичные данные
ffdemo/static/assets/images/firefox_logo.jpg Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 3.1 KiB

Двоичные данные
ffdemo/static/assets/images/flag.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 1.2 KiB

Двоичные данные
ffdemo/static/assets/images/footer_bg.gif Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 106 B

Двоичные данные
ffdemo/static/assets/images/i.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 970 B

Двоичные данные
ffdemo/static/assets/images/large_dash.gif Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 1.1 KiB

Двоичные данные
ffdemo/static/assets/images/light_overlay.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 1.0 KiB

Двоичные данные
ffdemo/static/assets/images/loader_light.gif Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 2.1 KiB

Двоичные данные
ffdemo/static/assets/images/loading.gif Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 2.0 KiB

Двоичные данные
ffdemo/static/assets/images/locale_selector_arrow.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 1.1 KiB

Двоичные данные
ffdemo/static/assets/images/location.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 2.0 KiB

Двоичные данные
ffdemo/static/assets/images/location_selector_dropdown_bg.gif Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 1.6 KiB

Двоичные данные
ffdemo/static/assets/images/location_small.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 1.2 KiB

Двоичные данные
ffdemo/static/assets/images/magnifying.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 3.0 KiB

Двоичные данные
ffdemo/static/assets/images/mark_up_logo.jpg Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 12 KiB

Двоичные данные
ffdemo/static/assets/images/mozilla_tab.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 3.3 KiB

Двоичные данные
ffdemo/static/assets/images/nextMark.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 1.3 KiB

Двоичные данные
ffdemo/static/assets/images/overlay.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 197 B

Двоичные данные
ffdemo/static/assets/images/playback.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 1.5 KiB

Двоичные данные
ffdemo/static/assets/images/prevMark.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 1.3 KiB

Двоичные данные
ffdemo/static/assets/images/reset.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 1.5 KiB

Двоичные данные
ffdemo/static/assets/images/site_thumb.jpg Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 2.5 KiB

Двоичные данные
ffdemo/static/assets/images/submit.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 1.1 KiB

Двоичные данные
ffdemo/static/assets/images/tri.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 993 B

Двоичные данные
ffdemo/static/assets/images/tri_rtl.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 1010 B

Двоичные данные
ffdemo/static/assets/images/twitter.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 2.1 KiB

Двоичные данные
ffdemo/static/assets/images/web_of_wonders.jpg Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 2.8 KiB

Двоичные данные
ffdemo/static/assets/images/world_eater.jpg Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 2.1 KiB

Двоичные данные
ffdemo/static/assets/images/zoomIn.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 1.2 KiB

Двоичные данные
ffdemo/static/assets/images/zoomOut.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 1.4 KiB

Просмотреть файл

@ -0,0 +1,161 @@
( function( $ ) {
var app = $.sammy( '#sammy', function() {
// ROUTES
this.get( '#/', function( context ) {
// making our mark
// unload the visualization if it's loaded
$( '#markapp' ).markApp( 'unloadModule', 'linear' );
// load the template
this.partial( 'makemark_sammy.html' )
.then( function() {
// init the intro
// init the capture interface, specifying that we're playing the intro first, and it should be ready to go when it's done
$( '#markapp' ).markApp( 'addModule', { 'capture': { 'state': 'intro' } } );
$( '#markapp' ).markApp( 'addModule', { 'intro': { } } );
$( '#sammy' ).css( 'zIndex', '' );
} );
} );
// MARK CREATION
this.get( '#/mark/new', function( context ) {
// unload the visualization if it's loaded
$( '#markapp' ).markApp( 'unloadModule', 'linear' );
var modOptions = {
'state': 'drawing',
'invite_code': context.params['invite'],
'contributor_type': context.params['contributor_type']
}
// if we already have the content loaded, just update the state of the interface
if ( $( '#markmaker' ).size() > 0 ) {
$( '#markapp' ).markApp( 'unloadModule', 'intro' );
$( '#markapp' ).markApp( 'addModule', { 'capture': modOptions } );
} else {
// template is not yet loaded, so lets start fresh
// load the template
this.partial( 'makemark_sammy.html' )
.then( function() {
// init the capture interface, specifying that the intro should not be shown
$( '#markapp' ).markApp( 'addModule', { 'capture': modOptions } );
$( '#sammy' ).css( 'zIndex', '' );
} );
}
} );
// MODERATION PAGE
this.get( '#/moderate', function( context ) {
// unload the other modules if they're loaded
$( '#markapp' ).markApp( 'unloadModule', 'intro' );
$( '#markapp' ).markApp( 'unloadModule', 'capture' );
this.partial( '/en/moderate_sammy.html' )
.then( function() {
// load the linear module
$( '#markapp' ).markApp( 'addModule', { 'linear': { 'is_flagged': true, 'linear_root': 'moderate' } } );
} );
} );
// Moderation via direct reference
this.get( '#\/moderate\/(.*)', function( context ) {
// unload the other modules if they're loaded
$( '#markapp' ).markApp( 'unloadModule', 'intro' );
$( '#markapp' ).markApp( 'unloadModule', 'capture' );
if ( $( '#linear' ).size() > 0 ) {
// already setup, just load the new reference mark into the module
$( '#markapp' ).markApp( 'addModule', { 'linear': { 'is_flagged': true, 'linear_root': 'moderate', 'reference_mark': context.params['splat'][0], 'playback': context.params['playback'] } } );
} else {
// show all the signatures
this.partial( '/en/moderate_sammy.html' )
.then( function() {
$( '#sammy' ).css( 'zIndex', '' );
$( '#markapp' ).css( { 'zIndex': 100, 'cursor': 'default' } );
// load up the visualization
$( '#markapp' ).markApp( 'addModule', { 'linear': { 'is_flagged': true, 'linear_root': 'moderate', 'reference_mark': context.params['splat'][0], 'playback': context.params['playback'] } } );
} );
}
} );
// visualization
// visualization with country filtering
this.get( '#/linear/country/:country_code\/(.*)', function( context ) {
// unload the other modules if they're loaded
$( '#markapp' ).markApp( 'unloadModule', 'intro' );
$( '#markapp' ).markApp( 'unloadModule', 'capture' );
if ( $( '#linear' ).size() > 0 ) {
// already setup, just load the new reference mark into the module
$( '#markapp' ).markApp( 'addModule', { 'linear': { 'country_code': context.params['country_code'], 'reference_mark': context.params['splat'][0] } } );
} else {
this.partial( 'linear_sammy.html' )
.then( function() {
$( '#sammy' ).css( 'zIndex', '' );
$( '#markapp' ).css( { 'zIndex': 100, 'cursor': 'default' } );
// load up the visualization
$( '#markapp' ).markApp( 'addModule', { 'linear': { 'country_code': context.params['country_code'], 'reference_mark': context.params['splat'][0] } } );
} );
}
} );
// visualization without country filtering
this.get( '#\/linear\/(.*)', function( context ) {
// unload the other modules if they're loaded
$( '#markapp' ).markApp( 'unloadModule', 'intro' );
$( '#markapp' ).markApp( 'unloadModule', 'capture' );
if ( $( '#linear' ).size() > 0 ) {
// already setup, just load the new reference mark into the module
$( '#markapp' ).markApp( 'addModule', { 'linear': { 'reference_mark': context.params['splat'][0], 'playback': context.params['playback'] } } );
} else {
// show all the signatures
this.partial( 'linear_sammy.html' )
.then( function() {
$( '#sammy' ).css( 'zIndex', '' );
$( '#markapp' ).css( { 'zIndex': 100, 'cursor': 'default' } );
// load up the visualization
$( '#markapp' ).markApp( 'addModule', { 'linear': { 'reference_mark': context.params['splat'][0], 'playback': context.params['playback'] } } );
} );
}
} );
// event handlers
this.bind( 'run', function() {
// add an instance of markApp
$( '#markapp' )
.markApp()
// and privide it a way of accessing the sammy app
.data( 'markApp-context' )['app'] = app;
} );
// other stuff
this.swap = function( content ) {
this.$element().fadeOut( 'fast', function() {
$( this ).html( content ).fadeIn( 'normal' );
$( '#markapp' ).trigger( 'ready' );
} );
$( '#markapp' ).trigger( 'swap' );
};
this.use( 'Template' );
} );
$( document ).ready( function () {
function browserSupportsRequiredFeatures() {
// detect canvas support
return !!document.createElement('canvas').getContext;
}
if ( browserSupportsRequiredFeatures ) {
// remove the placeholder content
$( '#fallback' ).remove();
// run the app
app.run( '#/' );
}
// Try binding click event to locale here
$("#current-locale").click(function ()
{
$(this).parent().find("ul").toggle();
$(this).toggleClass("selected");
return false;
});
} ); //document ready
} )( jQuery );

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

Просмотреть файл

@ -0,0 +1,32 @@
$(document).ready(function ()
{
$("#community").hover(function ()
{
$("#coming-soon-tip").toggle();
});
// Query for countries if newsletter
if ($("#newsletter-countries").length > 0)
{
$.ajax( {
'url': '/media/assets/js/vendor/country_codes.json',
'dataType': 'JSON',
'success': function ( data )
{
for( var i = 0; i < data.length; i++ )
{
var $select = $( '#newsletter-countries' );
var $option = $( '<option />' )
.val( data[i].code )
.text( data[i].name );
$select.append( $option );
}
},
'error': function ()
{
// handle error loading countries
}
});
}
});

8316
ffdemo/static/assets/js/jquery-1.5.1.js поставляемый Normal file

Разница между файлами не показана из-за своего большого размера Загрузить разницу

16
ffdemo/static/assets/js/jquery-1.5.1.min.js поставляемый Normal file

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

Разница между файлами не показана из-за своего большого размера Загрузить разницу

190
ffdemo/static/assets/js/markApp.min.js поставляемый Normal file
Просмотреть файл

@ -0,0 +1,190 @@
(function(a){a.markApp={modules:{},instances:[],fn:{}};a.fn.markApp=function(){var d=a(this),b=d.data("markApp-context");if(!b||typeof b=="undefined")b={app:null,$container:d,frameCount:0,width:0,height:0,minWidth:700,minHeight:500,countries:[],mouseX:null,mouseY:null,mouseDown:!1,mouseIn:!1,modules:{},usersMark:null,translatedStrings:{},locale:window.location.pathname.split("/").length>1?window.location.pathname.split("/")[1]:"en",instance:a.markApp.instances.push(d)-1,evt:{resize:function(){var d=
a(window).width(),c=a(window).height()-(a("header").height()+a("#callout-boxes").height());if(d<b.minWidth)d=b.minWidth;if(c<b.minHeight)c=b.minHeight;b.$container.parent().width(d);b.$container.parent().height(c);b.width=d;b.height=c;a(".autoResize").height(c).width(d).trigger("resize.markApp",[d,c])},mousemove:function(a){b.mouseX=a.layerX;b.mouseY=a.layerY},mousedown:function(a){"preventDefault"in a&&a.preventDefault();b.mouseDown=!0},mouseup:function(a){"preventDefault"in a&&a.preventDefault();
b.mouseDown=!1},mouseover:function(a){"preventDefault"in a&&a.preventDefault();b.mouseIn=!0},mouseout:function(a){"preventDefault"in a&&a.preventDefault();b.mouseX=null;b.mouseY=null;b.mouseIn=!1},ready:function(){b.fn.loadTranslations()}},public_fn:{addModule:function(b,d){var c,e={};if(typeof d=="string")c=d;else if(typeof d=="object")for(var j in d){c=j;e=d[j];break}a.markApp.modules[c]&&(b.modules[c]=b.modules[c]||{},"init"in a.markApp.modules[c].fn&&a.markApp.modules[c].fn.init(b,e))},unloadModule:function(b,
d){if(d=="all")for(d in b.modules)"deinit"in a.markApp.modules[d].fn&&a.markApp.modules[d].fn.deinit(b),delete b.modules[d];else d in b.modules&&("deinit"in a.markApp.modules[d].fn&&a.markApp.modules[d].fn.deinit(b),delete b.modules[d])}},fn:{trigger:function(d,c,e){typeof c=="undefined"&&(c={type:"custom"});if(d in b.evt&&b.evt[d](c)==!1)return!1;for(var i in b.modules)if(i in a.markApp.modules&&"evt"in a.markApp.modules[i]&&d in a.markApp.modules[i].evt)a.markApp.modules[i].evt[d](b,c,e)},loop:function(){setTimeout(function(){b.fn.loop()},
42);b.frameCount++;b.fn.trigger("loop",{},[])},withCountryCodes:function(d){"US"in b.countries?d(b.countries):a.ajax({url:"/media/assets/js/vendor/country_codes.json",dataType:"JSON",success:function(a){b.countries=a;for(var c=0;c<a.length;c++)b.countries[a[c].code]=a[c].name;d(b.countries)},error:function(){}})},showLoader:function(d,c){c=typeof c==="string"?c:"";d=typeof d==="string"?d:b.fn.getString("default-loading-msg");var e=a("<div />").width(b.width).height(b.height).hide().addClass("overlay-wrapper autoResize").addClass(c).attr("id",
"markapp-loader").append(a("<div />").text(d));b.$container.append(e);e.fadeIn("fast")},hideLoader:function(){a("#markapp-loader").fadeOut("fast",function(){a(this).remove()})},showError:function(d){d=typeof d==="string"?d:b.fn.getString("default-error-msg");d=a("<div />").width(b.width).height(b.height).hide().click(function(a){a.preventDefault();b.fn.hideError()}).addClass("overlay-wrapper autoResize").attr("id","markapp-error").append(a("<div />").attr("id","markapp-error-content").append(a("<p />").html(d)));
b.$container.append(d);d.fadeIn("fast")},hideError:function(){a("#markapp-error").fadeOut("fast",function(){a(this).remove()})},loadTranslations:function(){a("div.translated-strings").each(function(){a(this).children().each(function(){var d=a(this);d.is("ol")?(b.translatedStrings[d.attr("id")]=[],d.children().each(function(){b.translatedStrings[d.attr("id")].push(a(this).html())})):b.translatedStrings[d.attr("id")]=d.html()})})},getString:function(a,d){return a in b.translatedStrings?typeof b.translatedStrings[a]===
"object"&&typeof d==="number"?b.translatedStrings[a][d]:b.translatedStrings[a]:a},storeData:function(a,b){if(typeof localStorage!="undefined")try{typeof b==="object"&&(b=JSON.stringify(b)),localStorage.setItem(a,b)}catch(d){}},getData:function(a){if(typeof localStorage!="undefined")return(a=localStorage.getItem(a))?(a[0]=="{"&&(a=JSON.parse(a)),a):!1}}},a(window).delayedBind(300,"resize",function(a){return b.fn.trigger("resize",a)}).bind("keydown keypress keyup",function(a){return b.fn.trigger(a.type,
a)}).trigger("resize"),b.$container.bind("mousemove mousedown mouseup mouseover mouseout ready swap",function(a){return b.fn.trigger(a.type,a)}),b.fn.loop();var c=a.makeArray(arguments);if(c.length>0){var e=c.shift();if(e in b.public_fn)b.public_fn[e](b,typeof c[0]=="undefined"?{}:c[0])}return d.data("markApp-context",b)}})(jQuery);
(function(a){markApp=a.markApp=a.markApp||{};modules=a.markApp.modules=a.markApp.modules||{};modules.linear={defaults:{reference_mark:null,country_code:null,playback:!1,is_flagged:!1,linear_root:"linear"},config:{marks:{},flaggings:{},orderedMarks:[],leftBuffer:[],rightBuffer:[],bufferSize:20,bufferMinSize:5,scene:null,cameraChange:{},tweens:{},layerManager:null,initialized:!1,requestingMarks:!1,moreLeft:!0,moreRight:!0,hoverMark:null,currentMark:null,playbackTimes:{},eventChange:!1},evt:{ready:function(a){modules.linear.fn.initInterface(a)},
resize:function(a){a.modules.linear.eventChange=!0;a.modules.linear.layerManager.resizeAll(a.width,a.height)},keydown:function(a,b){var c=a.modules.linear;switch(b.keyCode){case 38:b.preventDefault();c.cameraChange.aZ=10;break;case 40:b.preventDefault();c.cameraChange.aZ=-10;break;case 39:b.preventDefault();c.cameraChange.aX=10;modules.linear.fn.hideMarkInformation(a);break;case 37:b.preventDefault(),c.cameraChange.aX=-10,modules.linear.fn.hideMarkInformation(a)}},keyup:function(a,b){var c=a.modules.linear;
switch(b.keyCode){case 38:case 40:b.preventDefault();c.cameraChange.aZ=0;break;case 39:case 37:b.preventDefault(),c.cameraChange.aX=0}},keypress:function(a,b){switch(b.keyCode){case 38:case 40:case 39:case 37:b.preventDefault()}},mousedown:function(a){var b=a.modules.linear;if(mark=modules.linear.fn.hitTest(a,a.mouseX,a.mouseY))mark==b.currentMark?modules.linear.fn.centerCurrentMark(a,function(){modules.linear.fn.showMarkInformation(a)}):b.country_code?a.app.setLocation("#/"+b.linear_root+"/country/"+
b.country_code+"/"+mark.reference):a.app.setLocation("#/"+b.linear_root+"/"+mark.reference)},mousemove:function(d){var b=d.modules.linear;b.eventChange=!0;if(mark=modules.linear.fn.hoverTest(d,d.mouseX,d.mouseY,b.hoverMark)){if(b.hoverMark)b.hoverMark.color=b.hoverMark.contributor_name?"0,139,211":"0,0,0";b.hoverMark=mark;b.currentMark&&b.hoverMark.reference==b.currentMark.reference&&b.hoverMark.contributor_name&&a("#mark-information").is(":visible")?a("#contributor-quote-box").fadeIn("fast").css({left:d.mouseX-
15,top:d.mouseY-a("#contributor-quote-box").height()-15}):(a("#contributor-quote-box:visible").fadeOut("fast"),b.hoverMark.color=b.hoverMark.contributor_name?"255,111,40":"0,139,211")}else if(b.hoverMark)b.hoverMark.color=b.hoverMark.contributor_name?"0,139,211":"0,0,0",b.hoverMark=null,a("#contributor-quote-box:visible").fadeOut("fast")},loop:function(d){var b=d.modules.linear,c=b.layerManager.layers.drawingLayer,e={x:b.scene.camera.position.x,y:b.scene.camera.position.y,z:b.scene.camera.position.z};
if("cameraEase"in b.tweens)TWEEN.update();else{b.cameraChange.vZ+=b.cameraChange.aZ;b.cameraChange.vX+=b.cameraChange.aX;b.cameraChange.vX*=0.93;b.cameraChange.vZ*=0.93;b.scene.camera.position.x+=b.cameraChange.vX;b.scene.camera.position.z+=b.cameraChange.vZ;var f=modules.linear.fn.closestMark(d);if(f){var g=b.scene.camera.position.y-(f.position.y+f.bHeight/2);g!=0&&Math.abs(g)>=10&&(b.scene.camera.position.y+=g>0?-10:10)}}if(!b.eventChange&&e.x==b.scene.camera.position.x&&e.y==b.scene.camera.position.y&&
e.z==b.scene.camera.position.z)return!1;if(b.hoverMark&&!modules.linear.fn.hoverTest(d,d.mouseX,d.mouseY,b.hoverMark))b.hoverMark.color=b.hoverMark.contributor_name?"0,139,211":"0,0,0",b.hoverMark=null,a("#contributor-quote-box:visible").fadeOut("fast");c.clean();modules.linear.fn.updateScene(d,b.scene.camera.position.x-1E4,b.scene.camera.position.x+1E4);modules.linear.fn.updateBuffers(d,b.scene.camera.position.x-1E4,b.scene.camera.position.x+1E4);Mark.renderer.renderScene(b.scene,{cursor:{x:d.mouseX,
y:d.mouseY},width:d.width,height:d.height});b.eventChange=!1;for(f in b.scene.timers)d=(new Date).getTime(),b.scene.timers[f].end<d&&delete b.scene.timers[f],b.eventChange=!0}},fn:{init:function(d,b){var c=d.modules.linear;if("$linear"in d.modules.linear){b.country_code!=c.country_code&&modules.linear.fn.dumpAllMarks(d);a.extend(c,c,b);for(option in modules.linear.defaults)b[option]==null&&(c[option]=modules.linear.defaults[option]);modules.linear.fn.updateInterface(d);modules.linear.fn.initMarks(d)}else a.extend(c,
modules.linear.defaults,b),a.extend(c,c,modules.linear.config),c.$linear=a("<div />").addClass("linear-container"),d.$container.append(c.$linear),c.scene=new Mark.scene,c.cameraChange={aX:0,aY:0,aZ:0,vX:0,vY:0,vZ:0},c.layerManager=new Mark.layerManager(c.$linear.get(0)),c.layerManager.addLayer("drawingLayer"),c.scene.canvasContext=c.layerManager.layers.drawingLayer.context,d.fn.trigger("resize"),c.initialized=!0;c.flaggings=d.fn.getData("markFlaggings")||{}},deinit:function(a){var b=a.modules.linear;
b.$linear.fadeOut("fast",function(){b.layerManager.removeAll();b.$linear.remove();b.initialized=!1})},initInterface:function(d){var b=d.modules.linear;a("#stats").hide();a("#mark-browsing-zoom-in a, #mark-browsing-zoom-out a, #mark-browsing-next a, #mark-browsing-prev a").click(function(a){a.preventDefault()}).bind("mouseup mouseout",function(b){b.preventDefault();if(a(this).data("mouseDown")){if(a(this).is("#mark-browsing-zoom-in a, #mark-browsing-zoom-out a"))d.modules.linear.cameraChange.aZ=0;
else if(a(this).is("#mark-browsing-next a, #mark-browsing-prev a"))d.modules.linear.cameraChange.aX=0;a(this).data("mouseDown",!1)}}).bind("mousedown",function(b){b.preventDefault();a(this).data("mouseDown",!0);if(a(this).is("#mark-browsing-zoom-in a, #mark-browsing-zoom-out a"))d.modules.linear.cameraChange.aZ=a(this).is("#mark-browsing-zoom-in a")?10:-10;else if(a(this).is("#mark-browsing-next a, #mark-browsing-prev a"))d.modules.linear.cameraChange.aX=a(this).is("#mark-browsing-next a")?10:-10,
modules.linear.fn.hideMarkInformation(d)});d.fn.withCountryCodes(function(c){for(var e=a("#country-select"),f=0;f<c.length;f++){var g=a("<option />").val(c[f].code).text(c[f].name);e.append(g)}e.change(function(){var c=a(this).val();c.length!=2&&b.country_code?d.app.setLocation("#/"+b.linear_root+"/"):c.length==2&&c!=b.country_code&&d.app.setLocation("#/"+b.linear_root+"/country/"+c+"/");a(this).blur();d.$container.focus()});b.country_code&&e.val(b.country_code)});a("#mark-information").hide();a("#mark-playback").click(function(a){a.preventDefault();
modules.linear.fn.replayCurrentMark(d)});a("#mark-flag").click(function(a){a.preventDefault();modules.linear.fn.flagCurrentMark(d)});a("#delete-mark").click(function(a){a.preventDefault();modules.linear.fn.deleteCurrentMark(d)});a("#approve-mark-checkbox").change(function(b){b.preventDefault();b=a(this).is(":checked");modules.linear.fn.approveCurrentMark(d,b)});a("#twitter-share").socialShare({share_url:"http://twitter.com/share",share_params:{text:d.fn.getString("twitter-msg")}});a("#facebook-share").socialShare({share_url:"http://www.facebook.com/sharer.php",
share_params:{t:d.fn.getString("facebook-msg")}});modules.linear.fn.updateInterface(d);modules.linear.fn.initMarks(d);a("#sammy #country-select").selectBox({autoWidth:!1});a("#sammy #contributor-select").selectBox({autoWidth:!1})},initMarks:function(a){var b=a.modules.linear;b.reference_mark&&b.reference_mark!=""?b.reference_mark in b.marks?modules.linear.fn.jumpToMark(a,b.reference_mark,b.playback):modules.linear.fn.loadMarks(a,{reference:b.reference_mark,include_forward:20,include_back:20,include_mark:1,
success:function(c){c.success?(modules.linear.fn.setupMarks(a,c.marks),modules.linear.fn.jumpToMark(a,b.reference_mark,b.playback)):a.fn.showError(b.errorMsg);b.requestingMarks=!1}}):modules.linear.fn.loadMarks(a,{offset:0,max:20,success:function(c){if(c.success){if(modules.linear.fn.setupMarks(a,c.marks),firstMark=b.marks[c.marks[0].reference])b.scene.camera.position.x=-4E3,b.scene.camera.position.z=-3E3,c="cameraEase"in b.tweens?b.tweens.cameraEase:new TWEEN.Tween(b.scene.camera.position),c.to({x:firstMark.bWidth/
2,y:firstMark.bHeight/2,z:-1E3},2E3).onComplete(function(){delete b.tweens.cameraEase;typeof callback==="function"&&callback(this)}).easing(TWEEN.Easing.Quartic.EaseInOut).start(),b.tweens.cameraEase=c}else a.fn.showError(b.errorMsg);b.requestingMarks=!1}})},updateInterface:function(d){var b=d.modules.linear,c=d.fn.getData("userMark");c&&(b.country_code?c.country_code==b.country_code:1)?a("#your-mark-link").attr("href","#/"+b.linear_root+"/"+d.fn.getData("userMark").reference).show():a("#your-mark-link").hide();
var e={};b.country_code?(e.country_code=b.country_code,a("#contributor-select").next().hide(),a("#contributor-select-label").hide()):(a("#contributor-select").next().show(),a("#contributor-select-label").show());a("#mark-browsing-options").is(".country-"+(b.country_code?b.country_code:"all"))||a.ajax({url:"/requests/init_viz_data",data:e,dataType:"JSON",success:function(c){a("#mark-browsing-options").removeAttr("class").addClass("country-"+(b.country_code?b.country_code:"all"));a("#stats-number-of-marks").text(c.total_marks);
a("#stats-number-of-countries").text(c.total_countries);var g=new Date(c.first_mark_at);g=Math.ceil(((new Date).getTime()-g.getTime())/864E5);a("#stats-number-of-days").text(g);a("#stats").fadeIn("fast");b.country_code?(a("#first-mark-link").attr("href","#/"+b.linear_root+"/country/"+b.country_code+"/"+c.country_first_mark),a("#last-mark-link").attr("href","#/"+b.linear_root+"/country/"+b.country_code+"/"+c.country_last_mark)):(a("#first-mark-link").attr("href","#/"+b.linear_root+"/"+c.first_mark),
a("#last-mark-link").attr("href","#/"+b.linear_root+"/"+c.last_mark));a("#mark-browsing").collapsibleMod();a("#stats").collapsibleMod({collapsedHeight:10});if(a("#contributor-select option").size()==1){g=a("#contributor-select");if(c.contributor_marks)for(var h=0;h<c.contributor_marks.length;h++){var i=a("<option />").val(c.contributor_marks[h].reference).text(c.contributor_marks[h].contributor);g.append(i)}g.change(function(){var c=a(this).val();c.length!="label"&&d.app.setLocation("#/"+b.linear_root+
"/"+c);a(this).blur();d.$container.focus()});b.country_code?(e.country_code=b.country_code,a("#contributor-select").next().hide(),a("#contributor-select-label").hide()):(a("#contributor-select").next().show(),a("#contributor-select-label").show())}}})},dumpAllMarks:function(a){a=a.modules.linear;for(mark in a.marks)delete a.marks[mark];a.scene.objects=[];a.leftBuffer=[];a.rightBuffer=[];a.moreRight=!0;a.moreLeft=!0;a.scene.camera.position.x=0;a.scene.camera.position.y=0;a.scene.camera.position.z=
-1E3},loadMarks:function(d,b){var c=d.modules.linear,e=b.success;delete b.success;if(c.country_code)b.country_code=c.country_code;var f=b.reference?"/requests/marks_by_reference":"/requests/all_marks";c.is_flagged&&(f="/requests/marks_by_flagged");b.reference&&!(b.reference in c.marks)?(modules.linear.fn.dumpAllMarks(d),d.fn.showLoader(d.fn.getString("loading-marks-msg"),"overlay-light")):b.reference||d.fn.showLoader(d.fn.getString("loading-marks-msg"),"overlay-light");a.ajax({url:f,data:b,dataType:"JSON"}).success(e).success(function(){d.fn.hideLoader()})},
setupMarks:function(a,b){var c=a.modules.linear;if(b.length!=0){for(var e=0;e<b.length;e++)b[e].reference in c.marks&&b.shift(e,1);e=[];for(var f in c.marks)e.push([f,c.marks[f].id]);e.sort(function(a,b){return a[1]-b[1]});var g=e.length==0||e[0][1]<b[0].id?c.rightBuffer:c.leftBuffer,h=g==c.leftBuffer?!0:!1;h&&b.reverse();var i=g.length>0?c.marks[g[h?0:g.length-1]]:c.scene.objects[h?0:c.scene.objects.length-1];for(e=0;e<b.length;e++)if(!(b[e].reference in c.marks)){var j=JSON.parse(b[e].points_obj_simplified);
if("strokes"in j&&!(j.strokes.length==0||j.strokes[0].length<2)){f=new Mark.gmlMark(j.strokes,b[e].reference,b[e].country_code,b[e].date_drawn,j.rtl,b[e].id,b[e].is_approved);if(b[e].contributor)f.contributor_name=b[e].contributor,f.extra_info=j.extra_info,f.color="0,139,211";c.marks[f.reference]=f;i&&f.positionRelativeTo(i,h);h?g.unshift(f.reference):g.push(f.reference);i=f}}}},refillBuffer:function(a,b){var c=a.modules.linear;if(!c.requestingMarks){c.requestingMarks=!0;var e=b==c.leftBuffer,f=null;
f=b.length>0?c.marks[b[e?0:b.length-1]]:c.scene.objects[e?0:c.scene.objects.length-1];modules.linear.fn.loadMarks(a,{reference:f.reference,include_forward:e?0:20,include_back:e?20:0,include_mark:0,success:function(b){b.success?(modules.linear.fn.setupMarks(a,b.marks),b.marks.length==0&&(c[e?"moreLeft":"moreRight"]=!1)):c[e?"moreLeft":"moreRight"]=!1;c.requestingMarks=!1}})}},updateBuffers:function(a,b,c){var e=a.modules.linear;if(e.scene.objects.length!=0){for(var f=e.scene.objects[0];f&&f.position.x+
f.bWidth<b;)e.leftBuffer.push(e.scene.objects.shift().reference),f=e.scene.objects[0];for(f=e.scene.objects[e.scene.objects.length-1];f&&f.position.x>c;)e.rightBuffer.unshift(e.scene.objects.pop().reference),f=e.scene.objects[e.scene.objects-1];e.leftBuffer.length<5&&e.scene.objects.length>0&&e.moreLeft&&!e.requestingMarks?modules.linear.fn.refillBuffer(a,e.leftBuffer):e.rightBuffer.length<5&&e.scene.objects.length>0&&e.moreRight&&!e.requestingMarks&&modules.linear.fn.refillBuffer(a,e.rightBuffer)}},
updateScene:function(a,b,c){a=a.modules.linear;if(a.rightBuffer.length>0)for(var e=a.marks[a.rightBuffer[0]];e&&e.position&&e.position.x<c;)a.scene.objects.push(a.marks[a.rightBuffer.shift()]),e=a.rightBuffer[0];if(a.leftBuffer.length>0)for(e=a.marks[a.leftBuffer[a.leftBuffer.length-1]];e&&e.position&&e.position.x+e.bWidth>b;)a.scene.objects.unshift(a.marks[a.leftBuffer.pop()]),e=a.leftBuffer[a.leftBuffer.length-1]},jumpToMark:function(a,b,c){var e=a.modules.linear;e.currentMark=e.marks[b];if(c){var f=
(new Date).getTime()*2;e.scene.timers[b]={start:f,end:f+e.currentMark.maxTime,speed:1}}modules.linear.fn.centerCurrentMark(a,function(){c&&modules.linear.fn.replayCurrentMark(a);modules.linear.fn.showMarkInformation(a)})},closestMark:function(a){a=a.modules.linear;for(var b=a.scene.objects[0],c=1;c<a.scene.objects.length;c++){var e=a.scene.objects[c];Math.abs(a.scene.camera.position.x-(e.position.x+e.bWidth/2))<Math.abs(a.scene.camera.position.x-(b.position.x+b.bWidth/2))&&(b=e)}return b},hoverTest:function(a,
b,c,e){if(e&&e.renderedBounds&&b>=e.renderedBounds.minX&&b<=e.renderedBounds.maxX&&c>=e.renderedBounds.minY&&c<=e.renderedBounds.maxY)return e;return modules.linear.fn.hitTest(a,b,c)},hitTest:function(a,b,c){a=a.modules.linear;for(var e=0;e<a.scene.objects.length;e++)if(a.scene.objects[e].renderedBounds&&b>=a.scene.objects[e].renderedBounds.minX&&b<=a.scene.objects[e].renderedBounds.maxX&&c>=a.scene.objects[e].renderedBounds.minY&&c<=a.scene.objects[e].renderedBounds.maxY)return a.scene.objects[e];
return!1},showMarkInformation:function(d){var b=d.modules.linear;if(!b.currentMark)return!1;var c=b.currentMark;a("#mark-id").text(c.id);var e=new Date(c.time),f=[];f.push(d.fn.getString("month-abbreviations",e.getMonth())+" "+e.getDate());f.push(e.getHours()+":"+(String(e.getMinutes()).length==1?"0"+e.getMinutes():e.getMinutes()));c.country_code?d.fn.withCountryCodes(function(b){a("#mark-country").text(" / "+b[c.country_code])}):a("#mark-country").text("");c.contributor_name?(a("#mark-contributor-name").text("- "+
c.contributor_name),a("#mark-flag").hide(),a("#contributor-quote").text(c.extra_info).html("&#8220;"+a("#contributor-quote").text()+"&#8221;"),a("#contributor-name").text("- "+c.contributor_name)):(a("#mark-contributor-name, #contributor-quote, #contributor-name").text(""),a("#mark-flag").show());a("#mark-timestamp").text(f.join(" / "));a("#url-share input").val(window.location.href);if(b.linear_root!="moderate")a("#twitter-share").data("socialShare-context").share_params.url=window.location.href,
a("#facebook-share").data("socialShare-context").share_params.u=window.location.href;b.linear_root=="moderate"&&a("#approve-mark-checkbox").attr("checked",c.is_approved);b.currentMark.reference in b.flaggings?a("#mark-flag").addClass("disabled"):a("#mark-flag").removeClass("disabled");a("#mark-information").fadeIn("fast")},hideMarkInformation:function(){a("#mark-information").fadeOut("fast")},replayCurrentMark:function(a){a=a.modules.linear;var b=(new Date).getTime();a.eventChange=!0;a.scene.timers[a.currentMark.reference]=
{start:b,end:b+a.currentMark.maxTime,speed:1}},flagCurrentMark:function(d){var b=d.modules.linear;b.currentMark.reference in b.flaggings||a.ajax({url:"/requests/flag_mark",data:{reference:b.currentMark.reference},type:"POST",dataType:"JSON",success:function(){a("#mark-flag").addClass("disabled");b.flaggings[b.currentMark.reference]=!0;d.fn.storeData("markFlaggings",b.flaggings)},error:function(){d.fn.showError(b.errorMsg)}})},deleteCurrentMark:function(d){var b=d.modules.linear;a.ajax({url:"/requests/delete_mark",
data:{reference:b.currentMark.reference},type:"POST",dataType:"JSON",success:function(){var a=b.currentMark.reference,e=null;delete b.marks[b.currentMark.reference];b.currentMark=null;for(var f=0;f<b.scene.objects.length;f++)if(e||b.scene.objects[f].reference==a){if(!e)e=f,b.scene.objects.splice(f,1),b.currentMark=b.scene.objects[f];f==0?b.scene.objects[f].positionToStart():b.scene.objects[f].positionRelativeTo(b.scene.objects[f-1])}d.app.setLocation("#/"+b.linear_root+"/"+b.currentMark.reference);
b.eventChange=!0},error:function(){d.fn.showError(b.errorMs)}})},approveCurrentMark:function(d,b){var c=d.modules.linear;a.ajax({url:"/requests/approve_mark",data:{reference:c.currentMark.reference,should_approve:b},type:"POST",dataType:"JSON",success:function(){c.currentMark.is_approved=b},error:function(){d.fn.showError(c.errorMsg)}})},centerCurrentMark:function(a,b){var c=a.modules.linear;if(!c.currentMark)return!1;modules.linear.fn.showMarkInformation(a);c.cameraChange.aX=0;c.cameraChange.vX=
0;c.cameraChange.aZ=0;c.cameraChange.vZ=0;var e=Math.abs(c.currentMark.position.x-c.scene.camera.position.x)/2;e=Math.max(1E3,e);var f="cameraEase"in c.tweens?c.tweens.cameraEase:new TWEEN.Tween(c.scene.camera.position);f.to({x:c.currentMark.position.x+c.currentMark.bWidth/2,y:c.currentMark.position.y+c.currentMark.bHeight/2,z:c.currentMark.position.z-1E3},e).onComplete(function(){delete c.tweens.cameraEase;typeof b==="function"&&b(this)}).easing(e>1200?TWEEN.Easing.Quadratic.EaseInOut:TWEEN.Easing.Quartic.EaseInOut).start();
c.tweens.cameraEase=f}}}})(jQuery);
(function(a){a.markApp=a.markApp||{};var d=a.markApp.modules=a.markApp.modules||{};d.capture={defaults:{state:"intro",invite_code:null,locale:null,contributor_type:null},config:{captureLimit:300,layerManager:null,capturedPoints:0,strokes:[],framecount:0,cleanedStrokes:[],lastX:null,lastY:null,captureTime:null,rtl:null,initialized:!1,currentStroke:null,mark:null,timeBetweenStrokes:400,events:[]},evt:{resize:function(a){var c=a.modules.capture;c.layerManager.resizeAll(a.width,a.height);if(c.mark&&c.mark.strokes.length>
0)for(var e=0;e<c.mark.strokes.length;e++)d.capture.fn.drawStroke(a,c.mark.strokes[e])},mousemove:function(a){a.mouseDown&&a.modules.capture.state=="drawing"&&d.capture.fn.capturePoint(a)},mousedown:function(b){switch(b.modules.capture.state){case "drawing":a("#location-dialog").is(":visible")&&a("#location-dialog").fadeOut("fast");b.modules.capture.mark||d.capture.fn.startMark(b);b.modules.capture.currentStroke||d.capture.fn.startStroke(b);break;case "intro":b.modules.capture.mark||d.capture.fn.startMark(b),
b.modules.capture.currentStroke||d.capture.fn.startStroke(b),d.capture.fn.endIntro(b)}},mouseup:function(a){a.modules.capture.state=="drawing"&&d.capture.fn.endStroke(a)},ready:function(b){var c=b.modules.capture;a("#markmaker").hide().children().hide();a("#markmaker-reset a").addClass("disabled").bind("mousedown",function(a){a.preventDefault();d.capture.fn.reset(b)});a("#markmaker-submit a").addClass("disabled").bind("mousedown",function(a){a.preventDefault();d.capture.fn.submit(b)});b.fn.withCountryCodes(function(b){for(var c=
a("#markmaker-country"),d=0;d<b.length;d++){var h=a("<option />").val(b[d].code).text(b[d].name);c.append(h)}});a("#markmaker-location a").bind("mousedown",{context:b},d.capture.fn.locationDialogToggle);a("#markmaker-information").bind("mouseover",{context:b},d.capture.fn.informationDialogToggle).bind("mouseout",{context:b},d.capture.fn.informationDialogToggle);c.state=="drawing"&&d.capture.fn.initDrawing(b);a("#sammy #markmaker-country").selectBox({autoWidth:!1})},loop:function(a){var c=a.modules.capture;
if(c.initialized)switch(c.frameCount++,d.capture.fn.commonLoop(a),a.modules.capture.state){case "drawing":d.capture.fn.drawLoop(a)}}},fn:{init:function(b,c){var e=b.modules.capture;if("$capture"in e){if("state"in c)d.capture.fn.reset(b),c.state=="drawing"&&b.mouseDown&&b.fn.trigger("mousedown"),e.state=c.state,d.capture.fn.initDrawing(b)}else a.extend(e,d.capture.defaults),a.extend(e,c),a.extend(e,d.capture.config),e.$capture=a("<div />").addClass("capture-container"),b.$container.css({zIndex:100,
cursor:"none"}).append(e.$capture),e.layerManager=new Mark.layerManager(e.$capture.get(0)),e.layerManager.addLayer("drawnLayer"),e.layerManager.addLayer("liveDrawingLayer"),b.fn.trigger("resize"),e.initialized=!0},deinit:function(a){var c=a.modules.capture;c.$capture.fadeOut("fast",function(){c.layerManager.removeAll();c.$capture.remove();c.initialized=!1})},initIntro:function(){},initDrawing:function(b){var c=b.modules.capture;a("#browse-marks").is(":visible")&&(a("#browse-marks, #click-anywhere, #intro-main-copy").fadeOut("fast"),
a("#markmaker-legal-line").fadeIn("slow"));a("#markmaker").css("background-position","0 "+(b.height-140)+"px");a("#markmaker").unbind("resize.markApp").bind("resize.markApp",function(){a("#markmaker").css("background-position","0 "+(b.height-140)+"px");a("#location-dialog:visible").size()>0&&a("#location-dialog:visible").css({bottom:a("#markmaker-location").height()+25,left:a("#markmaker-location").offset().left+32})});a("#markmaker").is(":visible")?a("#markmaker-controls").fadeIn("slow",function(){a("#markmaker-information").fadeIn("slow")}):
a("#markmaker").width(0).show().animate({width:b.width},"slow",function(){a("#markmaker-controls").fadeIn("slow",function(){a("#markmaker-information").fadeIn("slow")});a("#markmaker-legal-line").fadeIn("slow")});c.invite_code&&c.contributor_type=="t"?(c.captureLimit=1E3,c.$capture.addClass("translator"),a("#translator-fields").find("#translator-locale").text("'"+b.locale+"'").end().collapsibleMod().fadeIn("slow")):c.invite_code&&c.contributor_type=="c"&&(c.$capture.addClass("contributor"),a("#contributor-fields").collapsibleMod().fadeIn("slow"))},
endIntro:function(a){a.app.setLocation("#/mark/new")},locationDialogToggle:function(b){b.preventDefault();a("#location-dialog").is(":visible")?a("#location-dialog").fadeOut("fast"):a("#location-dialog").fadeIn("fast").css({bottom:a("#markmaker-location").height()+25,left:a("#markmaker-location").offset().left+32})},informationDialogToggle:function(b){b.preventDefault();b.type=="mouseout"?a("#information-dialog").fadeOut("fast"):a("#information-dialog").fadeIn("fast").css({bottom:a("#markmaker-information").height()+
36,left:a("#markmaker-information").offset().left-a("#information-dialog").width()+30})},startMark:function(b){var c=b.modules.capture;c.captureTime=(new Date).getTime();c.rtl=b.mouseX>a(window).width()/2;c.mark=new Mark.gmlMark([],"","",c.captureTime,c.rtl);a("#markmaker-submit a, #markmaker-reset a").removeClass("disabled")},endMark:function(a){a.modules.capture.mark.setupVars()},startStroke:function(a){a=a.modules.capture;a.currentStroke=[];if(a.strokes.length>0)a.captureTime=(new Date).getTime()-
(a.strokes[a.strokes.length-1][a.strokes[a.strokes.length-1].length-1].time+a.timeBetweenStrokes)},endStroke:function(a){var c=a.modules.capture;if(c.currentStroke.length>2){c.strokes.push(c.currentStroke);var e=Mark.simplification.simplifyPath(c.currentStroke,1);e=Mark.simplification.weightPath(e,[5,10,20,40]);c.mark.strokes.push(e);c.cleanedStrokes.push(e);d.capture.fn.drawStroke(a,e);c.capturedPoints-=c.currentStroke.length-e.length}c.currentStroke=null},capturePoint:function(a){var c=a.modules.capture;
if(c.capturedPoints>c.captureLimit)a.fn.trigger("mouseup"),d.capture.fn.closeShop(a);else{var e=(new Date).getTime();a=new Mark.gmlPoint(a.mouseX,a.mouseY,e-c.captureTime,0);c.currentStroke.length>0?(e=c.currentStroke[c.currentStroke.length-1],a.speed=e.speedToPoint(a),a.setAngleFromPoint(e),a.smoothAgainst(e,0.01)):c.strokes.length>=1&&d.capture.fn.drawGuide(c.layerManager.layers.drawnLayer.context,c.lastX,c.lastY,a.x,a.y);c.currentStroke.push(a);c.lastX=a.x;c.lastY=a.y;c.capturedPoints++}},reset:function(b){b=
b.modules.capture;b.layerManager.layers.liveDrawingLayer.clean();b.layerManager.layers.drawnLayer.clean();b.capturedPoints=0;b.rtl=null;b.mouseDown=!1;b.lastX=null;b.lastY=null;b.strokes=[];b.currentStroke=null;b.mark=null;b.captureTime=null;b.state="drawing";a("#markmaker-submit a, #markmaker-reset a").addClass("disabled");a("#markapp").css({cursor:"none"});a("#markmaker-instructions").fadeIn()},closeShop:function(b){var c=b.modules.capture;c.state="preview";d.capture.fn.endMark(b);c.layerManager.layers.liveDrawingLayer.clean();
b=c.layerManager.layers.liveDrawingLayer.context;var e=c.lastX,f=c.lastY;b.strokeStyle="rgba(0,0,0,0.2)";b.lineWidth=1;b.beginPath();b.dashedLineTo(e,f,c.rtl?0:c.layerManager.layers.liveDrawingLayer.canvas.width,f,[7,5]);b.closePath();b.stroke();a("#markapp").css({cursor:"default"})},submit:function(b){var c=b.modules.capture;if(c.state!="submitting"){c.state!="preview"&&d.capture.fn.closeShop(b);c.state="submitting";a("#markmaker-submit a").addClass("disabled");var e={};e.rtl=c.rtl;e.strokes=c.strokes;
e=JSON.stringify(e);var f=JSON.stringify(c.mark),g=a("#markmaker-country").val()=="label"?"":a("#markmaker-country").val();b.fn.showLoader(b.fn.getString("submitting-mark"));var h={points_obj:e,points_obj_simplified:f,country_code:g};if(c.invite_code&&c.contributor_type=="t")h.contributor_locale=b.locale,h.invite=c.invite_code;else if(c.invite_code&&c.contributor_type=="c")h.contributor=a("#contributor-name").val(),c.mark.extra_info=a("#contributor-quote").val(),h.points_obj_simplified=JSON.stringify(c.mark),
h.invite=c.invite_code;a.ajax({url:"/requests/save_mark",data:h,type:"POST",dataType:"JSON",success:function(a){h.contributor_locale?b.app.setLocation("#/linear/"):(b.fn.storeData("userMark",{reference:a.mark_reference,country_code:g}),b.app.setLocation("#/linear/"+a.mark_reference+"?playback=true"));b.fn.hideLoader()},error:function(){b.fn.showError(b.fn.getString("submit-error"))}});return!1}},drawStroke:function(a,c){var d=a.modules.capture;Mark.thickBrush(d.layerManager.layers.drawnLayer.context,
[c]);d.layerManager.layers.drawnLayer.context.fillStyle="rgba(255,255,255,0.3)";d.layerManager.layers.drawnLayer.context.strokeStyle="rgba(255,255,255,0.3)";Mark.circleBrush(d.layerManager.layers.drawnLayer.context,[c])},drawGuide:function(a,c,d,f,g){a.strokeStyle="rgba(0,0,0,0.2)";a.lineWidth=1;a.beginPath();a.dashedLineTo(c,d,f,g,[7,5]);a.closePath();a.stroke()},drawCursor:function(a,c,d,f){a.strokeStyle="#ff5400";a.fillStyle="#000000";a.beginPath();a.moveTo(c,d);a.lineTo(c+1,d-8);a.lineTo(c+20,
d-27);a.lineTo(c+23,d-23);a.lineTo(c,d);a.closePath();a.stroke();f*=18.5;f+=4.5;a.beginPath();a.moveTo(c,d);a.lineTo(c+1,d-8);a.lineTo(c+(f-3),d-(f+4));a.lineTo(c+f,d-f);a.lineTo(c,d);a.closePath();a.fill()},commonLoop:function(a){var c=a.modules.capture;c.layerManager.layers.liveDrawingLayer.clean();a.mouseIn&&(c.state=="drawing"||c.state=="intro")&&d.capture.fn.drawCursor(c.layerManager.layers.liveDrawingLayer.context,a.mouseX,a.mouseY,(c.captureLimit-c.capturedPoints)/c.captureLimit)},introLoop:function(){},
drawLoop:function(b){var c=b.modules.capture;if(c.currentStroke&&c.currentStroke.length>0)Mark.thickBrush(c.layerManager.layers.liveDrawingLayer.context,[c.currentStroke]),c.layerManager.layers.liveDrawingLayer.context.fillStyle="rgba(255,255,255,0.3)",c.layerManager.layers.liveDrawingLayer.context.strokeStyle="rgba(255,255,255,0.3)",Mark.circleBrush(c.layerManager.layers.liveDrawingLayer.context,[c.currentStroke]);if(b.mouseIn&&!b.mouseDown){var e,f;c.strokes.length==0?(e=b.mouseX>a(window).width()/
2?c.layerManager.layers.liveDrawingLayer.canvas.width:0,f=b.mouseY):(e=c.lastX,f=c.lastY);d.capture.fn.drawGuide(c.layerManager.layers.liveDrawingLayer.context,e,f,b.mouseX,b.mouseY)}}}}})(jQuery);
(function(a){markApp=a.markApp=a.markApp||{};modules=a.markApp.modules=a.markApp.modules||{};modules.intro={defaults:{reference_mark:null},config:{marks:{},animationMarks:["vVR","myWb"],playbackTimes:{},vizScene:null,textScene:null,layerManager:null,initialized:!1,eventChange:!1,curLocaleMark:null,animationComplete:!1,tweens:{}},evt:{resize:function(a){a.modules.intro.layerManager.resizeAll(a.width,a.height);a.modules.intro.eventChange=!0;a.modules.intro.xAnimationComplete&&modules.intro.fn.drawX(a)},
loop:function(a){var b=a.modules.intro;TWEEN.update();b.layerManager.layers.viz.clean();Mark.renderer.renderScene(b.vizScene,{width:a.width,height:a.height});if(b.curLocaleMark||b.xMark){b.layerManager.layers.mainMark.clean();if(b.curLocaleMark){var c=500/b.curLocaleMark.bWidth;Mark.renderer.renderMark(b.layerManager.layers.mainMark.context,b.curLocaleMark,{offset:{x:a.width/2-115,y:a.height-240},scale:{x:c,y:c,thickness:c},color:"255,84,0",timer:b.textScene.timers[b.curLocaleMark.reference]})}b.xMark&&
Mark.renderer.renderMark(b.layerManager.layers.mainMark.context,b.xMark,{offset:{x:10,y:-100},scale:{x:(a.width/2-200)/b.xMark.bWidth,y:(a.height+200)/b.xMark.bHeight,thickness:5},color:"0,0,0",timer:b.textScene.timers[b.xMark.reference]})}},ready:function(d){a("#markmaker").hide().children().hide();a.when(modules.intro.fn.initInterface(d),modules.intro.fn.loadMarks(d)).then(function(){modules.intro.fn.runVizPreview(d)}).then(function(){modules.intro.fn.startDomAnimation(d)}).fail(function(){modules.intro.fn.simpleIntro(d)})}},
fn:{init:function(d,b){var c=d.modules.intro;if(c.initialized)for(option in a.extend(c,c,b),modules.intro.defaults)b[option]==null&&(c[option]=modules.intro.defaults[option]);else a.extend(c,modules.intro.defaults,b),a.extend(c,c,modules.intro.config),c.$intro=a("<div />").addClass("intro-container"),d.$container.append(c.$intro),c.vizScene=new Mark.scene,c.textScene=new Mark.scene,c.layerManager=new Mark.layerManager(c.$intro.get(0)),c.layerManager.addLayer("viz"),c.vizScene.canvasContext=c.layerManager.layers.viz.context,
c.layerManager.addLayer("mainMark"),c.textScene.canvasContext=c.layerManager.layers.mainMark.context,c.layerManager.addLayer("X"),d.fn.trigger("resize"),c.initialized=!0},deinit:function(a){var b=a.modules.intro;b.$intro.fadeOut("fast",function(){b.layerManager.removeAll();b.$intro.remove();b.initialized=!1})},initInterface:function(d){a("#markmaker").unbind("resize.markApp").bind("resize.markApp",function(b,c,d){b=c/2+485;d-=140;c=!1;a("#markmaker").css("background-position","0 "+d+"px");a("#markmaker").is(":visible")||
(c=!0,a("#markmaker, #browse-marks, #click-anywhere, #intro-main-copy").css({display:"block"}));a("#browse-marks").css({top:d-50,left:b-85});a("#click-anywhere").css({top:d+12,left:b-a("#intro-main-copy").width()});a("#intro-main-copy").css({top:d-a("#intro-main-copy").height()-100,left:b-a("#intro-main-copy").width()});c&&a("#markmaker, #browse-marks, #click-anywhere, #intro-main-copy").css({display:"none"})}).trigger("resize.markApp",[d.width,d.height]).width(0).height(d.height)},loadMarks:function(d){return a.ajax({url:"/requests/get_translated_marks",
dataType:"JSON"}).success(function(a){modules.intro.fn.setupMarks(d,a.marks)})},setupMarks:function(d,b){var c=d.modules.intro;if(!(typeof b==="undefined"||b.length==0)){a(c.layerManager.layers.viz.canvas).css("opacity",0);b.sort(function(){return Math.round(Math.random())-0.5});for(var e=null,f=0;f<b.length;f++)try{var g=JSON.parse(b[f].points_obj_simplified),h=new Mark.gmlMark(g.strokes,b[f].reference,b[f].country_code,b[f].date_drawn,g.rtl,b[f].id,b[f].is_approved);if(!c.currentMark)c.currentMark=
h;b[f].reference in c.marks||(c.marks[h.reference]=h);e&&h.positionRelativeTo(e,!1);if(b[f].contributor_locale==d.locale||!c.curLocaleMark){var i=(new Date).getTime()*3;c.curLocaleMark=new Mark.gmlMark(g.strokes,b[f].reference,b[f].country_code,b[f].date_drawn,g.rtl,b[f].id,b[f].is_approved);c.textScene.timers[c.curLocaleMark.reference]={start:i,end:i+c.curLocaleMark.maxTime,speed:2}}c.vizScene.objects.push(h);e=h}catch(j){}}},runVizPreview:function(d){var b=d.modules.intro;if(!(b.vizScene.objects.length<
3)){b.vizScene.camera.position.x=-2E3;b.vizScene.camera.position.z=-3E3;d=b.vizScene.objects[b.vizScene.objects.length-2];var c=new TWEEN.Tween(b.vizScene.camera.position);c.to({x:d.position.x+d.bWidth/2,y:d.position.y+d.bHeight/2,z:d.position.z-2E3},6E3).onComplete(function(){delete b.tweens.cameraEase}).easing(TWEEN.Easing.Quartic.EaseInOut).start();b.tweens.cameraEase=c;a(b.layerManager.layers.viz.canvas).animate({opacity:"1"},"slow").delay(2E3).animate({opacity:"0.1"},"slow");a("#markmaker").delay(3E3)}},
startDomAnimation:function(d){a("#markmaker").width(0).show().animate({width:d.width},"slow",function(){modules.intro.fn.startMarkAnimations(d);a("#intro-main-copy").fadeIn("slow");a("#click-anywhere").delay(200).fadeIn("slow");a("#browse-marks").delay(100).fadeIn("slow")})},startMarkAnimations:function(a){var b=a.modules.intro;modules.intro.fn.drawX(a);b.curLocaleMark&&(a=(new Date).getTime()+2E3,b.textScene.timers[b.curLocaleMark.reference]={start:a,end:a+b.curLocaleMark.maxTime,speed:2})},drawX:function(a){a=
a.modules.intro;a.xMark=new Mark.gmlMark({strokes:[[{x:177,y:0,z:0,time:51,speed:0,angle:0,significance:5},{x:129,y:60,z:0,time:255,speed:0.45069390943299864,angle:0.5880026035475675,significance:1},{x:123,y:65,z:0,time:271,speed:0.4931203163041915,angle:0.7853981633974483,significance:1},{x:103,y:89,z:0,time:339,speed:0.45069390943299864,angle:0.5880026035475675,significance:1},{x:56,y:139,z:0,time:503,speed:0.45073896963561083,angle:0.7853981633974483,significance:1},{x:38,y:162,z:0,time:584,speed:0.3572203090978693,
angle:0.46364760900080615,significance:1},{x:9,y:192,z:0,time:691,speed:0.3535533905932738,angle:0.7853981633974483,significance:1},{x:0,y:206,z:0,time:727,speed:0.45069390943299864,angle:0.5880026035475675,significance:5}],[{x:11,y:23,z:0,time:1178,speed:0,angle:0,significance:5},{x:30,y:49,z:0,time:1246,speed:0.32424352695503,angle:5.639684198386302,significance:1},{x:55,y:77,z:0,time:1321,speed:0.48790367901871773,angle:5.497787143782138,significance:1},{x:72,y:100,z:0,time:1367,speed:0.5216642390945547,
angle:5.695182703632019,significance:1},{x:85,y:113,z:0,time:1408,speed:0.5355917833779965,angle:5.497787143782138,significance:2},{x:154,y:175,z:0,time:1662,speed:0.3311927108182759,angle:5.497787143782138,significance:1},{x:186,y:196,z:0,time:1802,speed:0.2849548128987055,angle:5.497787143782138,significance:5}]],country_code:"",time:1300925747439,rtl:!1,maxTime:1802,reference:"",hoverState:!1,renderedBounds:null,id:null,contributor_name:null,extra_info:null,color:"0,0,0",hoverColor:"0,139,211",
x:454,y:199,position:{x:0,y:0,z:0},rotationAngle:{x:0,y:0,z:0},sX:0,sY:0,bWidth:186,bHeight:206}.strokes,"xMark");a.textScene.objects.push(a.xMark);var b=(new Date).getTime();a.textScene.timers[a.xMark.reference]={start:b,end:b+a.xMark.maxTime,speed:1}},simpleIntro:function(d){a("#markmaker").width(0).show().animate({width:d.width},"slow",function(){a("#intro-main-copy").fadeIn("slow");a("#click-anywhere").delay(200).fadeIn("slow");a("#browse-marks").delay(100).fadeIn("slow")})}}}})(jQuery);
(function(a){function d(a){return a.replace(/-/g,"--").replace(/ /g,"-")}a.fn.extend({delayedBind:function(b,c,e,f){var g=d(c);return this.each(function(){var d=this;a(this).data("_delayedBindBound-"+g+"-"+b)||(a(this).data("_delayedBindBound-"+g+"-"+b,!0),a(this).bind(c,function(){var c=a(this).data("_delayedBindTimerID-"+g+"-"+b);typeof c!="undefined"&&clearTimeout(c);c=setTimeout(function(){a(d).trigger("_delayedBind-"+g+"-"+b)},b);a(this).data("_delayedBindTimerID-"+g+"-"+b,c)}));a(this).bind("_delayedBind-"+
g+"-"+b,e,f)})},delayedBindCancel:function(b,c){var e=d(c);return this.each(function(){var c=a(this).data("_delayedBindTimerID-"+e+"-"+b);typeof c!="undefined"&&clearTimeout(c)})},delayedBindUnbind:function(b,c,e){var f=d(c);return this.each(function(){a(this).unbind("_delayedBind-"+f+"-"+b,e)})}})})(jQuery);
(function(a){a.collapsibleMod={cfg:{collapsedClass:"collapsibleMod-collapsed",expandedClass:"collapsibleMod-expanded",$header:null,expandedHeight:0,collapsedHeight:0,$content:null,collapsed:!1,stateKey:"",saveState:!0},fn:{init:function(d,b){var c=a(d),e=a.extend({},a.collapsibleMod.cfg,b);e.$container=c;e.$header=c.find("h3:first");e.$content=c.children().not("h3:first");e.expandedHeight=e.$content.height();e.$header.bind("click",function(b){b.preventDefault();a.collapsibleMod.fn.toggle(e)});e.saveState&&
e.$container.attr("id")!=""&&typeof localStorage!="undefined"?(e.stateKey="collapsibleMod-state-"+e.$container.attr("id"),a.collapsibleMod.fn.restoreState(e)):e.saveState=!1;e.collapsed?a.collapsibleMod.fn.collapse(e):e.$container.addClass(e.expandedClass);c.data("collapsibleMod-context",e)},collapse:function(d){d.$container.addClass(d.collapsedClass).removeClass(d.expandedClass);d.$content.animate({height:d.collapsedHeight},"fast",function(){d.collapsedHeight==0&&d.$content.hide()});d.collapsed=
!0;a.collapsibleMod.fn.saveState(d)},expand:function(d){d.$container.removeClass(d.collapsedClass).addClass(d.expandedClass);d.$content.show().animate({height:d.expandedHeight},"fast");d.collapsed=!1;a.collapsibleMod.fn.saveState(d)},saveState:function(a){if(a.saveState)try{localStorage.removeItem(a.stateKey),localStorage.setItem(a.stateKey,a.collapsed)}catch(b){}},restoreState:function(a){if(a.saveState&&localStorage.getItem(a.stateKey))a.collapsed=localStorage.getItem(a.stateKey)==="true"},toggle:function(d){d.collapsed?
a.collapsibleMod.fn.expand(d):a.collapsibleMod.fn.collapse(d)}}};a.fn.collapsibleMod=function(d){return a(this).each(function(){a.collapsibleMod.fn.init(this,d)})}})(jQuery);
(function(a){a.socialShare={cfg:{$link:null,share_url:"http://twitter.com/share",share_title:"Share on Twitter",share_params:{},popupWidth:550,popupHeight:450},fn:{init:function(d,b){var c=a(d),e=a.extend({},a.socialShare.cfg,b);e.$link=c;e.$link.bind("click",function(b){b.preventDefault();a.socialShare.fn.share(e)});c.data("socialShare-context",e)},shareURL:function(a){var b=[];for(param in a.share_params)b.push(param+"="+encodeURIComponent(a.share_params[param]));return a.share_url+"?"+b.join("&")},
share:function(d){window.open(a.socialShare.fn.shareURL(d),d.share_title,"height="+d.popupHeight+",width="+d.popupWidth)}}};a.fn.socialShare=function(d){return a(this).each(function(){a.socialShare.fn.init(this,d)})}})(jQuery);
jQuery&&function(a){a.extend(a.fn,{selectBox:function(d,b){var c=function(b){var c=b.data.select,d=b.data.control;if(a(d).hasClass("ui-selectBox-disabled"))return!1;if(a(d).hasClass("ui-selectBox-focus")&&a("#ui-selectBox-dropdown").size()===1)return e(b,!0),!1;a(".ui-selectBox").not(d).trigger("blur");g(b);b.stopPropagation();a("#ui-selectBox-dropdown").remove();var m=a('<div id="ui-selectBox-dropdown" class="ui-corner-bottom" />'),r=a("<ul />");a(c).children("optgroup").size()===0?a(c).children("option").each(function(){var b=
a(this).text()!==""?a(this).text():"\u00a0",c="";a(this).attr("disabled")&&(c+=" ui-selectBox-disabled");a(r).append('<li class="ui-selectBox-option'+c+'">'+p(b)+"</li>")}):(a(m).addClass("ui-selectBox-hasOptgroups"),a(c).children("optgroup").each(function(){a(r).append('<li class="ui-selectBox-optgroup">'+p(a(this).attr("label"))+"</li>");a(this).children("option").each(function(){var b=a(this).text()!==""?a(this).text():"\u00a0",c="";a(this).attr("disabled")&&(c+=" ui-selectBox-disabled");a(r).append('<li class="ui-selectBox-option'+
c+'">'+p(b)+"</li>")})}));a(m).append(r);b=a(c)[0].selectedIndex;a(m).find("LI.ui-selectBox-option").eq(b).addClass("ui-selectBox-initial ui-selectBox-current");a(m).find("LI.ui-selectBox-option").hover(function(){a(m).find(".ui-selectBox-current").removeClass("ui-selectBox-current");a(this).addClass("ui-selectBox-current")},function(){a(this).removeClass("ui-selectBox-current")}).click({select:c,control:d},function(a){f(a)}).mouseup({select:c,control:d},function(b){a(b.target).trigger("click")});
a("BODY").append(m);c=a(d).offset();b=a(d).outerHeight();var h=a(d).outerWidth(),i=parseInt(a(m).css("borderLeftWidth"))+parseInt(a(m).css("borderRightWidth"));a(m).css({position:"absolute",zIndex:"999999",top:c.top+b,left:c.left,width:h-i}).show();a(d).removeClass("ui-corner-all").addClass("ui-corner-top");o(m);j(!0)},e=function(b,c){var d=b.data.control;a("#ui-selectBox-dropdown").remove();a(d).removeClass("ui-corner-top").addClass("ui-corner-all");c?a(d).focus():h(b)},f=function(b,c){var d=b.data.select,
m=b.data.control;c=c?c:b.target;if(a(c).hasClass("ui-selectBox-disabled"))return!1;var f=a(d)[0].selectedIndex;a("#ui-selectBox-dropdown .ui-selectBox-optgroup").remove();var g=a("#ui-selectBox-dropdown").find("LI.ui-selectBox-current").index();if(f!==g)a(d)[0].selectedIndex=g,a(m).find(".ui-selectBox-label").text(a(c).text()),a(d).trigger("change");e(b,!0)},g=function(b){var c=b.data.select;b=b.data.control;if(a(b).hasClass("ui-selectBox-disabled"))return!0;if(a(b).hasClass("ui-selectBox-focus"))return!1;
a(".ui-selectBox.ui-selectBox-focus").removeClass("ui-selectBox-focus");a("#ui-selectBox-dropdown").remove();a(b).addClass("ui-selectBox-focus");a(document).bind("mousedown",{select:c,control:b},h);a(document).bind("keydown",{select:c,control:b},i);a(c).trigger("focus");a(b).focus()},h=function(b){var c=b.data.select,d=b.data.control;if(b.target.id==="ui-selectBox-dropdown"||a(b.target).parents("#ui-selectBox-dropdown").size()===1)return a(d).trigger("focus"),!1;a(d).hasClass("ui-selectBox-focus")&&
(a(d).removeClass("ui-selectBox-focus"),a(document).unbind("mousedown",h),a(document).unbind("keydown",i),a(c).trigger("blur"),e(b))},i=function(b){var d=b.data.select,l=b.data.control,m=a("#ui-selectBox-dropdown");if(a(l).hasClass("ui-selectBox-disabled"))return!1;switch(b.keyCode){case 9:h(b);break;case 13:if(a(m).size()===0)return!1;d=a(m).find(".ui-selectBox-option");var g=-1;a.each(d,function(b,c){a(c).hasClass("ui-selectBox-current")&&(g=b)});g>=0&&f(b,a(d).eq(g));return!1;case 27:e(b,!0);break;
case 38:case 37:case 33:var k=b.keyCode===33?20:1;if(a(m).size()===0){if(b.altKey)return c(b),!1;b=a(d).find("OPTION").size();m=a(d)[0].selectedIndex;for(k=a(d)[0].selectedIndex-k;a(d).find("OPTION").eq(k).attr("disabled")===!0&&k>=0;)k--;k<0&&(k=a(d).find("OPTION:not([disabled]):first").index());a(d)[0].selectedIndex=k;if(a(d)[0].selectedIndex===-1)k=0,a(d)[0].selectedIndex=k;b=a(d).find("OPTION:selected").text();b===""&&(b="\u00a0");a(l).find(".ui-selectBox-label").text(b);k!==m&&a(d).trigger("change");
return!1}d=a(m).find(".ui-selectBox-option");g=-1;a.each(d,function(b,c){a(c).hasClass("ui-selectBox-current")&&(g=b)});g-=k;g<0&&(g=0);a(d).removeClass("ui-selectBox-current");a(d).eq(g).addClass("ui-selectBox-current");j();return!1;case 40:case 39:case 34:k=b.keyCode===34?20:1;if(a(m).size()===0){if(b.altKey)return c(b),!1;b=a(d).find("OPTION").size();m=a(d)[0].selectedIndex;for(k=a(d)[0].selectedIndex+k;a(d).find("OPTION").eq(k).attr("disabled")===!0&&k<=a(d).find("OPTION").size();)k++;k>b-1&&
(k=a(d).find("OPTION:not([disabled]):last").index());a(d)[0].selectedIndex=k;if(a(d)[0].selectedIndex===-1)k=a(d).find("OPTION").size()-1,a(d)[0].selectedIndex=k;b=a(d).find("OPTION:selected").text();b===""&&(b="\u00a0");a(l).find(".ui-selectBox-label").text(b);k!=m&&a(d).trigger("change");return!1}d=a(m).find(".ui-selectBox-option");g=-1;a.each(d,function(b,c){a(c).hasClass("ui-selectBox-current")&&(g=b)});g+=k;g>a(d).size()-1&&(g=a(d).size()-1);a(d).removeClass("ui-selectBox-current");a(d).eq(g).addClass("ui-selectBox-current");
j();return!1;case 36:case 35:if(a(m).size()===0){if(b.altKey)return c(b),!1;m=a(d)[0].selectedIndex;k=b.keyCode===36?0:a(d).find("OPTION").size()-1;a(d).find("OPTION").eq(k).attr("disabled")===!0&&(k=b.keyCode===36?a(d).find("OPTION:not([disabled]):first").index():a(d).find("OPTION:not([disabled]):last").index());a(d)[0].selectedIndex=k;b=a(d).find("OPTION:selected").text();b===""&&(b="\u00a0");a(l).find(".ui-selectBox-label").text(b);k!=m&&a(d).trigger("change");return!1}a(m).find(".ui-selectBox-current").removeClass("ui-selectBox-current");
b.keyCode===36?a(m).find(".ui-selectBox-option:first").addClass("ui-selectBox-current"):a(m).find(".ui-selectBox-option:last").addClass("ui-selectBox-current");j();return!1}},j=function(b){var c=a("#ui-selectBox-dropdown");if(a(c).size()===0)return!1;var d=a(c).find(".ui-selectBox-current");if(a(d).size()===0)return!1;var e=parseInt(a(d).offset().top-a(c).position().top),f=parseInt(e+a(d).outerHeight());b?a(c).scrollTop(a(d).offset().top-a(c).offset().top+a(c).scrollTop()-a(c).height()/2):(e<0&&a(c).scrollTop(a(d).offset().top-
a(c).offset().top+a(c).scrollTop()),f>a(c).height()&&a(c).scrollTop(a(d).offset().top+a(d).outerHeight()-a(c).offset().top+a(c).scrollTop()-a(c).height()))},o=function(b){a(b).css("MozUserSelect","none").bind("selectstart",function(){return!1}).bind("mousedown",function(){return!1});return!0},p=function(a){return a.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#039;")};switch(d){case "destroy":return a(this).each(function(){var b=a(this),c=a(this).next(".ui-selectBox");
a(b)[0].tagName.toLowerCase()==="select"&&(a(c).remove(),a(b).removeData("selectBox-options").show())}),a(this);case "disable":return a(this).each(function(){var b=a(this),c=a(this).next(".ui-selectBox");a(b).attr("disabled",!0);a(c).addClass("ui-selectBox-disabled")}),a(this);case "enable":return a(this).each(function(){var b=a(this),c=a(this).next(".ui-selectBox");a(b).attr("disabled",!1);a(c).removeClass("ui-selectBox-disabled")}),a(this);case "setOptions":if(!b)return a(this);a(this).each(function(){var c=
a(this);a(this).next(".ui-selectBox");switch(typeof b){case "string":a(c).html(b);break;case "object":for(var d in a(c).html(""),b)if(b[d]!==null){if(typeof b[d]==="object"){var l=a('<optgroup label="'+d+'" />'),e;for(e in b[d])a(l).append('<option value="'+e+'">'+b[d][e]+"</option>")}else l=a('<option value="'+d+'">'+b[d]+"</option>");a(c).append(l)}}d=a(c).data("selectBox-options");a(c).selectBox("destroy");a(c).selectBox(d)});return a(this);case "value":return a("#ui-selectBox-dropdown").remove(),
a(this).each(function(){var c=a(this),d=a(this).next(".ui-selectBox");a(c).val(b);c=a(c).find(":selected").text();c===""&&(c="\u00a0");a(d).removeClass("ui-corner-top").addClass("ui-corner-all").find(".ui-selectBox-label").text(c)}),a(this);default:return a(this).each(function(){d||(d={});var b=a.extend({autoWidth:!0},d),e=a(this);if(a(this).next(".ui-selectBox").size()===0){var l=a('<a href="#" class="ui-selectBox ui-corner-all" tabindex="'+parseInt(a(e).attr("tabindex"))+'" />');a(l).addClass(a(e).attr("class")).attr({style:(a(e).attr("style")+
"").replace(/inline/,"inline-block"),title:a(e).attr("title")});a(e).data("selectBox-options",b);if(b.autoWidth){var m="";a(e).find("OPTION").each(function(){a(this).text().length>m.length&&(m=a(this).text())});b=a('<div class="ui-selectBox-dropdown" style="position: absolute; top: -9999em; left: -9999em; width: auto; display: inline-block;" />');var f=a('<li class="ui-selectBox-option">'+p(m)+"</li>");a(b).append(f);a("BODY").append(b);a(l).width(f.outerWidth());a(b).remove()}a(e)[0].tagName.toLowerCase()!==
"select"||a(e).attr("multiple")===!0||(a(e).attr("disabled")===!0&&a(l).addClass("ui-selectBox-disabled"),b=a(e).find("OPTION:selected").text(),b===""&&(b="\u00a0"),a(l).append('<span class="ui-selectBox-label">'+p(b)+"</span>"),a(l).append('<span class="ui-selectBox-arrow"></span>'),a(e).hide().after(l),o(l),a(l).bind("click",function(){return!1}).bind("mousedown",{select:e,control:l},c).bind("focus",{select:e,control:l},g).bind("blur",{select:e,control:l},h))}}),a(this)}}})}(jQuery);
this.JSON||(JSON={});
(function(){function a(a){return a<10?"0"+a:a}function d(a){e.lastIndex=0;return e.test(a)?'"'+a.replace(e,function(a){var b=h[a];return typeof b==="string"?b:"\\u"+("0000"+a.charCodeAt(0).toString(16)).slice(-4)})+'"':'"'+a+'"'}function b(a,c){var e,h,q,l,m=f,r,k=c[a];k&&typeof k==="object"&&typeof k.toJSON==="function"&&(k=k.toJSON(a));typeof i==="function"&&(k=i.call(c,a,k));switch(typeof k){case "string":return d(k);case "number":return isFinite(k)?String(k):"null";case "boolean":case "null":return String(k);case "object":if(!k)return"null";
f+=g;r=[];if(Object.prototype.toString.apply(k)==="[object Array]"){l=k.length;for(e=0;e<l;e+=1)r[e]=b(e,k)||"null";q=r.length===0?"[]":f?"[\n"+f+r.join(",\n"+f)+"\n"+m+"]":"["+r.join(",")+"]";f=m;return q}if(i&&typeof i==="object"){l=i.length;for(e=0;e<l;e+=1)h=i[e],typeof h==="string"&&(q=b(h,k))&&r.push(d(h)+(f?": ":":")+q)}else for(h in k)Object.hasOwnProperty.call(k,h)&&(q=b(h,k))&&r.push(d(h)+(f?": ":":")+q);q=r.length===0?"{}":f?"{\n"+f+r.join(",\n"+f)+"\n"+m+"}":"{"+r.join(",")+"}";f=m;return q}}
if(typeof Date.prototype.toJSON!=="function")Date.prototype.toJSON=function(){return this.getUTCFullYear()+"-"+a(this.getUTCMonth()+1)+"-"+a(this.getUTCDate())+"T"+a(this.getUTCHours())+":"+a(this.getUTCMinutes())+":"+a(this.getUTCSeconds())+"Z"},String.prototype.toJSON=Number.prototype.toJSON=Boolean.prototype.toJSON=function(){return this.valueOf()};var c=/[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,e=/[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
f,g,h={"\u0008":"\\b","\t":"\\t","\n":"\\n","\u000c":"\\f","\r":"\\r",'"':'\\"',"\\":"\\\\"},i;if(typeof JSON.stringify!=="function")JSON.stringify=function(a,c,d){var e;g=f="";if(typeof d==="number")for(e=0;e<d;e+=1)g+=" ";else typeof d==="string"&&(g=d);if((i=c)&&typeof c!=="function"&&(typeof c!=="object"||typeof c.length!=="number"))throw Error("JSON.stringify");return b("",{"":a})};if(typeof JSON.parse!=="function")JSON.parse=function(a,b){function d(a,c){var e,f,g=a[c];if(g&&typeof g==="object")for(e in g)Object.hasOwnProperty.call(g,
e)&&(f=d(g,e),f!==void 0?g[e]=f:delete g[e]);return b.call(a,c,g)}var e;c.lastIndex=0;c.test(a)&&(a=a.replace(c,function(a){return"\\u"+("0000"+a.charCodeAt(0).toString(16)).slice(-4)}));if(/^[\],:{}\s]*$/.test(a.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,"@").replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,"]").replace(/(?:^|:|,)(?:\s*\[)+/g,"")))return e=eval("("+a+")"),typeof b==="function"?d({"":e},""):e;throw new SyntaxError("JSON.parse");}})();
CanvasRenderingContext2D.prototype.dashedLineTo=function(a,d,b,c,e){var f=function(a,b){return a<=b},g=function(a,b){return a>=b},h=function(a,b){return Math.min(a,b)},i=function(a,b){return Math.max(a,b)},j={thereYet:g,cap:h};g={thereYet:g,cap:h};if(d-c>0)g.thereYet=f,g.cap=i;if(a-b>0)j.thereYet=f,j.cap=i;this.moveTo(a,d);f=a;i=d;h=0;for(var o=!0;!j.thereYet(f,b)||!g.thereYet(i,c);){var p=Math.atan2(c-d,b-a),n=e[h];f=j.cap(b,f+Math.cos(p)*n);i=g.cap(c,i+Math.sin(p)*n);o?this.lineTo(f,i):this.moveTo(f,
i);h=(h+1)%e.length;o=!o}};CanvasRenderingContext2D.prototype.dottedArc=function(a,d,b,c,e,f){var g=Math.PI/b/2,h=c;for(c+=g;c<e;)this.beginPath(),this.arc(a,d,b,h,c,f),this.stroke(),h=c+g,c=h+g};
var Mark=function(a){a.layer=function(a,b){this.context=this.canvas=null;this.dirtyRectangles=[];this.layerName=b;this.manager=a;this.clean=function(){if(this.dirtyRectangles.length==0)this.context.clearRect(0,0,this.canvas.width,this.canvas.height);else for(var a=0;a<this.dirtyRectangles.length;a++)this.context.clearRect(a.x,a.y,a.w,a.h)};this.setSize=function(a,b){if(this.canvas.width!=a)this.canvas.width=a;if(this.canvas.height!=b)this.canvas.height=b};this.init=function(){this.canvas=document.createElement("canvas");
this.context=this.canvas.getContext("2d");this.setSize(this.manager.container.scrollWidth,this.manager.container.scrollHeight);this.manager.layerWrapper.appendChild(this.canvas)};this.remove=function(){this.manager.layerWrapper.removeChild(this.canvas)};this.init()};return a}(Mark||{});
Mark=function(a){a.layerManager=function(d){this.container=d;this.layerWrapper=null;this.layers={};this.init=function(){this.layerWrapper=document.createElement("div");this.layerWrapper.className="mark-layerManager";this.container.appendChild(this.layerWrapper)};this.addLayer=function(b){var c=new a.layer(this,b);return this.layers[b]=c};this.removeAll=function(){for(var a in this.layers)this.layers[a].remove(),delete this.layers[a]};this.resizeAll=function(a,c){for(var d in this.layers)this.layers[d].setSize(a,
c)};this.init()};return a}(Mark||{});
Mark=function(a){a.gmlPoint=function(a,b,c,e,f){this.x=a;this.y=b;this.z=typeof f=="integer"?f:0;this.time=c;this.speed=e;this.angle=0;this.significance=1;this.distanceToPoint=function(a){return Math.sqrt(Math.pow(a.x-this.x,2)+Math.pow(a.y-this.y,2))};this.speedToPoint=function(a){return this.distanceToPoint(a)/(a.time-this.time)};this.smoothAgainst=function(a,b){var c=this.distanceToPoint(a);c*=b;if(Math.abs(this.speed-a.speed)>c)this.speed=this.speed>a.speed?a.speed+c:a.speed-c};this.setAngleFromPoint=
function(a){this.angle=Math.atan2(a.y-this.y,a.x-this.x)+Math.PI/2;this.angle%=2*Math.PI;if(this.angle<0)this.angle=2*Math.PI+this.angle};this.clone=function(){return{x:this.x,y:this.y,z:this.z,time:this.time,significance:this.significance,angle:this.angle,speed:this.speed}};this.getTranslatedPoint=function(a,b){var c=this.clone();c.x+=a;c.y+=b;return c}};return a}(Mark||{});
Mark=function(a){a.simplification={Line:function(a,b){this.p1=a;this.p2=b;this.distanceToPoint=function(a){var b=(this.p2.y-this.p1.y)/(this.p2.x-this.p1.x),d=[];d.push(Math.abs(a.y-b*a.x-(this.p1.y-b*this.p1.x))/Math.sqrt(Math.pow(b,2)+1));d.push(Math.sqrt(Math.pow(a.x-this.p1.x,2)+Math.pow(a.y-this.p1.y,2)));d.push(Math.sqrt(Math.pow(a.x-this.p2.x,2)+Math.pow(a.y-this.p2.y,2)));return d.sort(function(a,b){return a-b})[0]}},douglasPeucker:function(d,b){var c=[];if(d.length<=2)return[d[0]];for(var e=
new a.simplification.Line(d[0],d[d.length-1]),f=0,g=0,h=1;h<=d.length-2;h++){var i=e.distanceToPoint(d[h]);i>f&&(f=i,g=h)}f>=b?(f=d[g],e.distanceToPoint(f,!0),c=c.concat(a.simplification.douglasPeucker(d.slice(0,g+1),b)),c=c.concat(a.simplification.douglasPeucker(d.slice(g,d.length),b))):(f=d[g],e.distanceToPoint(f,!0),c=[d[0]]);return c},simplifyPath:function(d,b){var c=a.simplification.douglasPeucker(d,b);c.push(d[d.length-1]);return c},weightPath:function(d,b){for(var c=b.length+1,e=d;tolerance=
b.shift();){e=a.simplification.douglasPeucker(e,tolerance);for(var f=0;f<e.length;f++)e[f].significance++}d[0].significance=c;d[d.length-1].significance=c;return d}};return a}(Mark||{});
Mark=function(a){a.dof=1E4;a.thickMarkBrush=function(d,b,c,e,f,g){e=e?e:"0,0,0";var h=Mark.renderer.translatePoint(b[0][0],c),i={minX:h.x,maxX:h.x,minY:h.y,maxY:h.y};if(!f||!g||!(h.x>f*2||h.x<-f||h.y>g*2||h.y<-g||h.z>a.dof||h.z<0)){for(f=0;f<b.length;f++)if(!(typeof b[f]=="undefined"||b[f].length<=1))for(var j=null,o=0;o<b[f].length;o++)if(h=Mark.renderer.translatePoint(b[f][o],c),!(h.z&&h.z>a.dof)&&!(h.significance&&h.significance*(a.dof/5)<h.z-500))if(j){d.lineWidth=1;if(o==b[f].length-1)var p=
0,n=0;else n=9-Math.max(0,Math.pow(h.speed+1,3)),c.mode=="flatScale"&&c.scale.thickness?n*=c.scale.thickness:h.z&&(n*=2/h.z*(g/2)),n<0.1&&(n=0.1),n+=1,p=Math.cos(h.angle)*n,n*=Math.sin(h.angle);d.strokeStyle="rgba("+e+","+(a.dof-h.z)/a.dof+")";d.fillStyle="rgba("+e+","+(a.dof-h.z)/a.dof+")";try{d.beginPath(),d.lineWidth=0.5*((a.dof-h.z)/a.dof),d.moveTo(j.x-prevPX-0.5,j.y-prevPY-0.5),d.lineTo(j.x+prevPX-0.5,j.y+prevPY-0.5),d.lineTo(h.x+p-0.5,h.y+n-0.5),d.lineTo(h.x-p-0.5,h.y-n-0.5),d.lineTo(j.x-prevPX-
0.5,j.y-prevPY-0.5),d.fill(),d.stroke()}catch(q){}i.minX=h.x<i.minX?h.x:i.minX;i.minY=h.y<i.minY?h.y:i.minY;i.maxX=h.x>i.maxX?h.x:i.maxX;i.maxY=h.y>i.maxY?h.y:i.maxY;j=h;prevPX=p;prevPY=n}else{if(f!=0&&h.z<1500&&(j=Mark.renderer.translatePoint(b[f-1][b[f-1].length-1],c),j.z&&j.z<a.dof))d.strokeStyle="rgba(0,0,0,0.3)",d.lineWidth=1,d.beginPath(),d.dashedLineTo(j.x,j.y,h.x,h.y,[6,4]),d.closePath(),d.stroke();j=h;prevPY=prevPX=0}return i}};a.connectionBrush=function(d,b,c,e,f,g,h){e={offset:e,w:g,h:h,
mode:"pinhole"};b=Mark.renderer.translatePoint(b,e);e.offset=f;c=Mark.renderer.translatePoint(c,e);if(!(b.x>g||b.x<0||b.y>h||b.y<0)||!(c.x>g||c.x<0||c.y>h||c.y<0))if(!b.z||!c.z||!(b.z>a.dof||b.z<0)||!(c.z>a.dof||c.z<0))d.strokeStyle="rgba(0,0,0,"+(a.dof-b.z)/(a.dof*2)+")",f=3*(2/b.z)*(h/2),f<1&&(f=1),d.lineWidth=f,d.beginPath(),d.moveTo(b.x,b.y),d.lineTo(c.x,c.y),d.closePath(),d.stroke()};a.thickBrush=function(a,b,c,e,f){c=c?c:0;e=e?e:0;f=f?f:1;for(var g=0;g<b.length;g++)if(!(typeof b[g]=="undefined"||
b[g].length<=1)){if(g>0)a.strokeStyle="rgba(0,0,0,0.1)",a.lineWidth=1,a.beginPath(),a.dashedLineTo(b[g-1][b[g-1].length-1].x+c,b[g-1][b[g-1].length-1].y+e,b[g][0].x+c,b[g][0].y+e,[6,4]),a.closePath(),a.stroke();a.lineWidth=1;a.strokeStyle="#000000";a.fillStyle="#000000";for(var h=0,i=0,j=b[g][0].x,o=b[g][0].y,p=1;p<b[g].length;p++){var n=b[g][p];if(!(n.significance<f)){if(p==b[g].length-1)var q=0,l=0;else l=9-Math.pow(n.speed+1,3),l<0.5&&(l=0.5),l+=1,q=Math.cos(n.angle)*l,l*=Math.sin(n.angle);try{a.beginPath(),
a.moveTo(j-h-0.5+c,o-i-0.5+e),a.lineTo(j+h-0.5+c,o+i-0.5+e),a.lineTo(n.x+q-0.5+c,n.y+l-0.5+e),a.lineTo(n.x-q-0.5+c,n.y-l-0.5+e),a.lineTo(j-h-0.5+c,o-i-0.5+e),a.fill(),a.stroke()}catch(m){}h=q;i=l;j=n.x;o=n.y;prevAng=n.angle}}}};a.circleMarkBrush=function(d,b,c){for(var e=3,f=0;f<b.length;f++)if(b[f].length!=0)for(var g=0;g<b[f].length;g++){var h=Mark.renderer.translatePoint(b[f][g],c);if(!(h.z&&h.z>a.dof))e=3*((a.dof-h.z)/a.dof),d.fillStyle="rgba(255,255,255,0.4)",d.strokeStyle="rgba(255,255,255,0.4)",
d.beginPath(),d.arc(h.x,h.y,e,0,Math.PI*2,!0),d.closePath(),d.fill(),d.stroke(),d.lineTo(h.x,h.y)}};a.circleBrush=function(a,b,c,e,f){c=c?c:0;e=e?e:0;f=f?f:1;for(var g=0;g<b.length;g++)if(b[g].length!=0){a.beginPath();for(var h=0;h<b[g].length;h++){var i=b[g][h];i.significance<f||(a.beginPath(),a.arc(i.x+c,i.y+e,3,0,Math.PI*2,!0),a.closePath(),a.fill(),a.stroke(),a.lineTo(i.x+c,i.y+e))}}};return a}(Mark||{});
Mark=function(a){a.gmlMark=function(a,b,c,e,f,g,h){this.strokes=a;this.country_code=c;this.time=e;this.rtl=f;this.maxTime=0;this.reference=b;this.hoverState=!1;this.renderedBounds=null;this.id=g?g:null;this.is_approved=h;this.extra_info=this.contributor_name=null;this.color="0,0,0";this.y=this.x=0;this.position={x:0,y:0,z:0};this.rotationAngle={x:0,y:0,z:0};this.bHeight=this.bWidth=this.sY=this.sX=0;this.init=function(){this.strokes.length>0&&this.setupVars()};this.setupVars=function(){this.maxTime=
this.lastPoint().time;this.getBoundingBox()};this.leftmostStrokeStart=function(){for(var a=this.strokes[0][0],b=1;b<this.strokes.length;b++){var c=this.strokes[b][0];c.x<a.x&&(lastPoint=c)}return a};this.rightmostStrokeEnd=function(){for(var a=this.strokes[0][this.strokes[0].length-1],b=1;b<this.strokes.length;b++){var c=this.strokes[b][this.strokes[b].length-1];c.x>a.x&&(a=c)}return a};this.firstPoint=function(){return this.strokes[0][0]};this.lastPoint=function(){return this.strokes[this.strokes.length-
1][this.strokes[this.strokes.length-1].length-1]};this.translatePoint=function(a){var b=a.clone();b.x=this.x+a.x;b.y=this.y+a.y;return b};this.getBoundingBox=function(){var a=this.strokes[0][0],b=a.x,c=a.x,d=a.y;a=a.y;for(var e=0;e<this.strokes.length;e++)for(var f=0;f<this.strokes[e].length;f++){var l=this.strokes[e][f];b=l.x>b?l.x:b;d=l.y>d?l.y:d;c=l.x<c?l.x:c;a=l.y<a?l.y:a}this.bWidth=b-c;this.bHeight=d-a;this.x=c;this.y=a;(c!=0||a!=0)&&this.fitPointsToBounds(c,a)};this.fitPointsToBounds=function(a,
b){for(var c=0;c<this.strokes.length;c++)for(var d=0;d<this.strokes[c].length;d++)this.strokes[c][d].x-=a,this.strokes[c][d].y-=b};this.strokesAtTime=function(a){if(a>this.maxTime)return this.strokes;for(var b=[[]],c=[0,0],d=this.strokes[c[0]][c[1]];d.time<a;)b[b.length-1].push(d),c[1]++,this.strokes[c[0]].length==c[1]&&(c[0]++,c[1]=0,b.push([])),d=this.strokes[c[0]][c[1]];return b};this.positionRelativeTo=function(a,b){b&&b?(this.position.x=a.position.x-50-this.bWidth,this.position.y=a.position.y+
a.leftmostStrokeStart().y-this.rightmostStrokeEnd().y,this.position.z=a.position.z-this.maxTime/50):(this.position.x=a.position.x+a.bWidth+this.leftmostStrokeStart().x+50,this.position.y=a.position.y+a.rightmostStrokeEnd().y-this.firstPoint().y,this.position.z=a.position.z+a.maxTime/50)};this.positionToStart=function(){this.position.x=0;this.position.y=0;this.position.z=0};this.init()};return a}(Mark||{});
Mark=function(a){a.scene=function(){this.camera=new Mark.camera;this.objects=[];this.canvasContext=null;this.timers={};this.init=function(){};this.addObject=function(a){this.objects.push(a)};this.removeObject=function(a){this.objects.splice(a,1)};this.update=function(){var d=(new Date).getTime();for(a in this.timers)this.timers[a].end<d&&delete this.timers[a]};this.init()};return a}(Mark||{});
Mark=function(a){a.renderer={translatePoint:function(a,b){var c={flatScale:function(a,b){var c="offset"in b&&"x"in b.offset?b.offset.x:0,d="offset"in b&&"y"in b.offset?b.offset.y:0,e="scale"in b&&"y"in b.scale?b.scale.y:1;a.x*="scale"in b&&"x"in b.scale?b.scale.x:1;a.y*=e;a.x+=c;a.y+=d;return a},pinhole:function(a,b){var c="offset"in b&&"y"in b.offset?b.offset.y:0,d="offset"in b&&"z"in b.offset?b.offset.z:0,e="w"in b?b.w:500,o="h"in b?b.h:500;a.x+="offset"in b&&"x"in b.offset?b.offset.x:0;a.y+=c;
a.z=a.x>0?Math.pow((a.x- -100)/100,2)+d:Math.pow((a.x- -100)/100/2,2)+d;a.time&&(a.z+=a.time/50);c=2/a.z;a.x=a.x*c*(o/2)+e/2;a.y=a.y*c*(o/2)+o/2;return a}},e={x:a.x,y:a.y,z:a.z,time:a.time,significance:a.significance,angle:a.angle,speed:a.speed};return"mode"in b&&b.mode in c?c[b.mode](e,b):c.pinhole(e,b)},strokesAtTime:function(a,b){for(var c=[[]],e=[0,0],f=a[e[0]][e[1]];f.time<b;){c[c.length-1].push(f);e[1]++;if(a[e[0]].length==e[1]){if(a.length==e[0]+1)break;e[0]++;e[1]=0;c.push([])}f=a[e[0]][e[1]]}return c},
renderScene:function(d,b){for(var c=b.width,e=b.height,f=d.objects.length-1;f>=0;f--){var g={x:d.objects[f].position.x-d.camera.position.x,y:d.objects[f].position.y-d.camera.position.y,z:d.objects[f].position.z-d.camera.position.z};colorBase=d.objects[f].color;var h={offset:g,w:b.width,h:b.height,mode:"pinhole"};if(f<d.objects.length-1&&!(d.objects[f].reference in d.timers)){h={x:d.objects[f+1].position.x-d.camera.position.x,y:d.objects[f+1].position.y-d.camera.position.y,z:d.objects[f+1].position.z-
d.camera.position.z};var i={x:d.objects[f].position.x-d.camera.position.x,y:d.objects[f].position.y-d.camera.position.y,z:d.objects[f].position.z-d.camera.position.z},j=d.objects[f+1].leftmostStrokeStart(),o=d.objects[f].rightmostStrokeEnd();Mark.connectionBrush(d.canvasContext,j,o,h,i,c,e)}h={offset:g,w:b.width,h:b.height,mode:"pinhole"};if(d.objects[f].reference in d.timers){if((g=a.renderer.strokesAtTime(d.objects[f].strokes,((new Date).getTime()-d.timers[d.objects[f].reference].start)*d.timers[d.objects[f].reference].speed))&&
g.length>0&&g[0].length>0)d.objects[f].renderedBounds=Mark.thickMarkBrush(d.canvasContext,g,h,colorBase,b.width,b.height)}else d.objects[f].renderedBounds=Mark.thickMarkBrush(d.canvasContext,d.objects[f].strokes,h,colorBase,b.width,b.height)}},renderMark:function(d,b,c){var e={offset:c.offset,scale:c.scale,mode:"flatScale"};if("timer"in c&&c.timer){var f=a.renderer.strokesAtTime(b.strokes,((new Date).getTime()-c.timer.start)*c.timer.speed);if(f&&f.length>0&&f[0].length>0)b.renderedBounds=Mark.thickMarkBrush(d,
f,e,c.color)}else b.renderedBounds=Mark.thickMarkBrush(d,b.strokes,e,c.color)}};return a}(Mark||{});Mark=function(a){a.camera=function(){this.position=new a.vector(0,0,-1E3)};return a}(Mark||{});Mark=function(a){a.vector=function(a,b,c){this.x=a||0;this.y=b||0;this.z=c||0};return a}(Mark||{});
var TWEEN=TWEEN||function(){var a,d,b,c=[];this.add=function(a){c.push(a)};this.remove=function(b){a=c.indexOf(b);a!==-1&&c.splice(a,1)};this.update=function(){a=0;d=c.length;for(b=(new Date).getTime();a<d;)c[a].update(b)?a++:(c.splice(a,1),d--)};return this}();
TWEEN.Tween=function(a){var d={},b={},c={},e=1E3,f=0,g=null,h=TWEEN.Easing.Linear.EaseNone,i=null,j=null,o=null;this.to=function(b,d){d!==null&&(e=d);for(var f in b)a[f]!==null&&(c[f]=b[f]);return this};this.start=function(){TWEEN.add(this);g=(new Date).getTime()+f;for(var e in c)a[e]!==null&&(d[e]=a[e],b[e]=c[e]-a[e]);return this};this.stop=function(){TWEEN.remove(this);return this};this.delay=function(a){f=a;return this};this.easing=function(a){h=a;return this};this.chain=function(a){i=a};this.onUpdate=
function(a){j=a;return this};this.onComplete=function(a){o=a;return this};this.update=function(c){var f,q;if(c<g)return!0;c=(c-g)/e;c=c>1?1:c;q=h(c);for(f in b)a[f]=d[f]+b[f]*q;j!==null&&j.call(a,q);if(c==1)return o!==null&&o.call(a),i!==null&&i.start(),!1;return!0}};TWEEN.Easing={Linear:{},Quadratic:{},Cubic:{},Quartic:{},Quintic:{},Sinusoidal:{},Exponential:{},Circular:{},Elastic:{},Back:{},Bounce:{}};TWEEN.Easing.Linear.EaseNone=function(a){return a};
TWEEN.Easing.Quadratic.EaseIn=function(a){return a*a};TWEEN.Easing.Quadratic.EaseOut=function(a){return-a*(a-2)};TWEEN.Easing.Quadratic.EaseInOut=function(a){if((a*=2)<1)return 0.5*a*a;return-0.5*(--a*(a-2)-1)};TWEEN.Easing.Cubic.EaseIn=function(a){return a*a*a};TWEEN.Easing.Cubic.EaseOut=function(a){return--a*a*a+1};TWEEN.Easing.Cubic.EaseInOut=function(a){if((a*=2)<1)return 0.5*a*a*a;return 0.5*((a-=2)*a*a+2)};TWEEN.Easing.Quartic.EaseIn=function(a){return a*a*a*a};
TWEEN.Easing.Quartic.EaseOut=function(a){return-(--a*a*a*a-1)};TWEEN.Easing.Quartic.EaseInOut=function(a){if((a*=2)<1)return 0.5*a*a*a*a;return-0.5*((a-=2)*a*a*a-2)};TWEEN.Easing.Quintic.EaseIn=function(a){return a*a*a*a*a};TWEEN.Easing.Quintic.EaseOut=function(a){return(a-=1)*a*a*a*a+1};TWEEN.Easing.Quintic.EaseInOut=function(a){if((a*=2)<1)return 0.5*a*a*a*a*a;return 0.5*((a-=2)*a*a*a*a+2)};TWEEN.Easing.Sinusoidal.EaseIn=function(a){return-Math.cos(a*Math.PI/2)+1};
TWEEN.Easing.Sinusoidal.EaseOut=function(a){return Math.sin(a*Math.PI/2)};TWEEN.Easing.Sinusoidal.EaseInOut=function(a){return-0.5*(Math.cos(Math.PI*a)-1)};TWEEN.Easing.Exponential.EaseIn=function(a){return a==0?0:Math.pow(2,10*(a-1))};TWEEN.Easing.Exponential.EaseOut=function(a){return a==1?1:-Math.pow(2,-10*a)+1};TWEEN.Easing.Exponential.EaseInOut=function(a){if(a==0)return 0;if(a==1)return 1;if((a*=2)<1)return 0.5*Math.pow(2,10*(a-1));return 0.5*(-Math.pow(2,-10*(a-1))+2)};
TWEEN.Easing.Circular.EaseIn=function(a){return-(Math.sqrt(1-a*a)-1)};TWEEN.Easing.Circular.EaseOut=function(a){return Math.sqrt(1- --a*a)};TWEEN.Easing.Circular.EaseInOut=function(a){if((a/=0.5)<1)return-0.5*(Math.sqrt(1-a*a)-1);return 0.5*(Math.sqrt(1-(a-=2)*a)+1)};TWEEN.Easing.Elastic.EaseIn=function(a){var d,b=0.1,c=0.4;if(a==0)return 0;if(a==1)return 1;c||(c=0.3);!b||b<1?(b=1,d=c/4):d=c/(2*Math.PI)*Math.asin(1/b);return-(b*Math.pow(2,10*(a-=1))*Math.sin((a-d)*2*Math.PI/c))};
TWEEN.Easing.Elastic.EaseOut=function(a){var d,b=0.1,c=0.4;if(a==0)return 0;if(a==1)return 1;c||(c=0.3);!b||b<1?(b=1,d=c/4):d=c/(2*Math.PI)*Math.asin(1/b);return b*Math.pow(2,-10*a)*Math.sin((a-d)*2*Math.PI/c)+1};
TWEEN.Easing.Elastic.EaseInOut=function(a){var d,b=0.1,c=0.4;if(a==0)return 0;if(a==1)return 1;c||(c=0.3);!b||b<1?(b=1,d=c/4):d=c/(2*Math.PI)*Math.asin(1/b);if((a*=2)<1)return-0.5*b*Math.pow(2,10*(a-=1))*Math.sin((a-d)*2*Math.PI/c);return b*Math.pow(2,-10*(a-=1))*Math.sin((a-d)*2*Math.PI/c)*0.5+1};TWEEN.Easing.Back.EaseIn=function(a){return a*a*(2.70158*a-1.70158)};TWEEN.Easing.Back.EaseOut=function(a){return(a-=1)*a*(2.70158*a+1.70158)+1};
TWEEN.Easing.Back.EaseInOut=function(a){if((a*=2)<1)return 0.5*a*a*(3.5949095*a-2.5949095);return 0.5*((a-=2)*a*(3.5949095*a+2.5949095)+2)};TWEEN.Easing.Bounce.EaseIn=function(a){return 1-TWEEN.Easing.Bounce.EaseOut(1-a)};TWEEN.Easing.Bounce.EaseOut=function(a){return(a/=1)<1/2.75?7.5625*a*a:a<2/2.75?7.5625*(a-=1.5/2.75)*a+0.75:a<2.5/2.75?7.5625*(a-=2.25/2.75)*a+0.9375:7.5625*(a-=2.625/2.75)*a+0.984375};
TWEEN.Easing.Bounce.EaseInOut=function(a){if(a<0.5)return TWEEN.Easing.Bounce.EaseIn(a*2)*0.5;return TWEEN.Easing.Bounce.EaseOut(a*2-1)*0.5+0.5};
(function(a,d){var b,c=/:([\w\d]+)/g,e=/\?([^#]*)$/,f=function(a){return Array.prototype.slice.call(a)},g=function(a){return Object.prototype.toString.call(a)==="[object Function]"},h=function(a){return Object.prototype.toString.call(a)==="[object Array]"},i=function(a){return decodeURIComponent(a.replace(/\+/g," "))},j=encodeURIComponent,o=function(a){return String(a).replace(/&(?!\w+;)/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")},p=function(a){return function(b,c){return this.route.apply(this,
[a,b,c])}},n={},q=[];b=function(){var c=f(arguments),d,e;b.apps=b.apps||{};if(c.length===0||c[0]&&g(c[0]))return b.apply(b,["body"].concat(c));else if(typeof(e=c.shift())=="string")return d=b.apps[e]||new b.Application,d.element_selector=e,c.length>0&&a.each(c,function(a,b){d.use(b)}),d.element_selector!=e&&delete b.apps[e],b.apps[d.element_selector]=d};b.VERSION="0.6.3";b.addLogger=function(a){q.push(a)};b.log=function(){var c=f(arguments);c.unshift("["+Date()+"]");a.each(q,function(a,d){d.apply(b,
c)})};typeof d.console!="undefined"?g(d.console.log.apply)?b.addLogger(function(){d.console.log.apply(d.console,arguments)}):b.addLogger(function(){d.console.log(arguments)}):typeof console!="undefined"&&b.addLogger(function(){console.log.apply(console,arguments)});a.extend(b,{makeArray:f,isFunction:g,isArray:h});b.Object=function(b){return a.extend(this,b||{})};a.extend(b.Object.prototype,{escapeHTML:o,h:o,toHash:function(){var b={};a.each(this,function(a,c){g(c)||(b[a]=c)});return b},toHTML:function(){var b=
"";a.each(this,function(a,c){g(c)||(b+="<strong>"+a+"</strong> "+c+"<br />")});return b},keys:function(a){var b=[],c;for(c in this)(!g(this[c])||!a)&&b.push(c);return b},has:function(b){return this[b]&&a.trim(this[b].toString())!=""},join:function(){var a=f(arguments),b=a.shift();return a.join(b)},log:function(){b.log.apply(b,arguments)},toString:function(b){var c=[];a.each(this,function(a,d){(!g(d)||b)&&c.push('"'+a+'": '+d.toString())});return"Sammy.Object: {"+c.join(",")+"}"}});b.HashLocationProxy=
function(a,b){this.app=a;this.is_native=!1;this._startPolling(b)};b.HashLocationProxy.prototype={bind:function(){var c=this,e=this.app;a(d).bind("hashchange."+this.app.eventNamespace(),function(a,f){if(c.is_native===!1&&!f)b.log("native hash change exists, using"),c.is_native=!0,d.clearInterval(b.HashLocationProxy._interval);e.trigger("location-changed")});if(!b.HashLocationProxy._bindings)b.HashLocationProxy._bindings=0;b.HashLocationProxy._bindings++},unbind:function(){a(d).unbind("hashchange."+
this.app.eventNamespace());b.HashLocationProxy._bindings--;b.HashLocationProxy._bindings<=0&&d.clearInterval(b.HashLocationProxy._interval)},getLocation:function(){var a=d.location.toString().match(/^[^#]*(#.+)$/);return a?a[1]:""},setLocation:function(a){return d.location=a},_startPolling:function(c){var e=this;if(!b.HashLocationProxy._interval){c||(c=10);var f=function(){var c=e.getLocation();(!b.HashLocationProxy._last_location||c!=b.HashLocationProxy._last_location)&&d.setTimeout(function(){a(d).trigger("hashchange",
[!0])},13);b.HashLocationProxy._last_location=c};f();b.HashLocationProxy._interval=d.setInterval(f,c)}}};b.Application=function(a){var c=this;this.routes={};this.listeners=new b.Object({});this.arounds=[];this.befores=[];this.namespace=(new Date).getTime()+"-"+parseInt(Math.random()*1E3,10);this.context_prototype=function(){b.EventContext.apply(this,arguments)};this.context_prototype.prototype=new b.EventContext;g(a)&&a.apply(this,[this]);this._location_proxy||this.setLocationProxy(new b.HashLocationProxy(this,
this.run_interval_every));this.debug&&this.bindToAllEvents(function(a,b){c.log(c.toString(),a.cleaned_type,b||{})})};b.Application.prototype=a.extend({},b.Object.prototype,{ROUTE_VERBS:["get","post","put","delete"],APP_EVENTS:["run","unload","lookup-route","run-route","route-found","event-context-before","event-context-after","changed","error","check-form-submission","redirect","location-changed"],_last_route:null,_location_proxy:null,_running:!1,element_selector:"body",debug:!1,raise_errors:!1,run_interval_every:50,
template_engine:null,toString:function(){return"Sammy.Application:"+this.element_selector},$element:function(b){return b?a(this.element_selector).find(b):a(this.element_selector)},use:function(){var a=f(arguments),c=a.shift(),d=c||"";try{a.unshift(this),typeof c=="string"&&(d="Sammy."+c,c=b[c]),c.apply(this,a)}catch(e){typeof c==="undefined"?this.error("Plugin Error: called use() but plugin ("+d.toString()+") is not defined",e):g(c)?this.error("Plugin Error",e):this.error("Plugin Error: called use() but '"+
d.toString()+"' is not a function",e)}return this},setLocationProxy:function(a){var b=this._location_proxy;this._location_proxy=a;this.isRunning()&&(b&&b.unbind(),this._location_proxy.bind())},route:function(b,d,e){var f=this,h=[],i,j;!e&&g(d)&&(e=d=b,b="any");b=b.toLowerCase();if(d.constructor==String){for(c.lastIndex=0;(j=c.exec(d))!==null;)h.push(j[1]);d=RegExp("^"+d.replace(c,"([^/]+)")+"$")}typeof e=="string"&&(e=f[e]);i=function(a){var b={verb:a,path:d,callback:e,param_names:h};f.routes[a]=
f.routes[a]||[];f.routes[a].push(b)};b==="any"?a.each(this.ROUTE_VERBS,function(a,b){i(b)}):i(b);return this},get:p("get"),post:p("post"),put:p("put"),del:p("delete"),any:p("any"),mapRoutes:function(b){var c=this;a.each(b,function(a,b){c.route.apply(c,b)});return this},eventNamespace:function(){return["sammy-app",this.namespace].join("-")},bind:function(a,b,c){var d=this;typeof c=="undefined"&&(c=b);b=function(a,b){var e;b&&b.context?(e=b.context,delete b.context):e=new d.context_prototype(d,"bind",
a.type,b,a.target);a.cleaned_type=a.type.replace(d.eventNamespace(),"");c.apply(e,[a,b])};this.listeners[a]||(this.listeners[a]=[]);this.listeners[a].push(b);this.isRunning()&&this._listen(a,b);return this},trigger:function(a,b){this.$element().trigger([a,this.eventNamespace()].join("."),[b]);return this},refresh:function(){this.last_location=null;this.trigger("location-changed");return this},before:function(a,b){g(a)&&(b=a,a={});this.befores.push([a,b]);return this},after:function(a){return this.bind("event-context-after",
a)},around:function(a){this.arounds.push(a);return this},isRunning:function(){return this._running},helpers:function(b){a.extend(this.context_prototype.prototype,b);return this},helper:function(a,b){this.context_prototype.prototype[a]=b;return this},run:function(b){if(this.isRunning())return!1;var c=this;a.each(this.listeners.toHash(),function(b,d){a.each(d,function(a,d){c._listen(b,d)})});this.trigger("run",{start_url:b});this._running=!0;this.last_location=null;this.getLocation()==""&&typeof b!=
"undefined"&&this.setLocation(b);this._checkLocation();this._location_proxy.bind();this.bind("location-changed",function(){c._checkLocation()});this.bind("submit",function(b){return c._checkFormSubmission(a(b.target).closest("form"))===!1?b.preventDefault():!1});a(d).bind("beforeunload",function(){c.unload()});return this.trigger("changed")},unload:function(){if(!this.isRunning())return!1;var b=this;this.trigger("unload");this._location_proxy.unbind();this.$element().unbind("submit").removeClass(b.eventNamespace());
a.each(this.listeners.toHash(),function(c,d){a.each(d,function(a,d){b._unlisten(c,d)})});this._running=!1;return this},bindToAllEvents:function(b){var c=this;a.each(this.APP_EVENTS,function(a,d){c.bind(d,b)});a.each(this.listeners.keys(!0),function(a,d){c.APP_EVENTS.indexOf(d)==-1&&c.bind(d,b)});return this},routablePath:function(a){return a.replace(e,"")},lookupRoute:function(b,c){var d=this,e=!1;this.trigger("lookup-route",{verb:b,path:c});typeof this.routes[b]!="undefined"&&a.each(this.routes[b],
function(a,b){if(d.routablePath(c).match(b.path))return e=b,!1});return e},runRoute:function(b,c,d,e){var f=this,g=this.lookupRoute(b,c),h,j,n,o,p,q,s;this.log("runRoute",[b,c].join(" "));this.trigger("run-route",{verb:b,path:c,params:d});typeof d=="undefined"&&(d={});a.extend(d,this._parseQueryString(c));if(g){this.trigger("route-found",{route:g});if((q=g.path.exec(this.routablePath(c)))!==null)q.shift(),a.each(q,function(a,b){if(g.param_names[a])d[g.param_names[a]]=i(b);else{if(!d.splat)d.splat=
[];d.splat.push(i(b))}});h=new this.context_prototype(this,b,c,d,e);e=this.arounds.slice(0);n=this.befores.slice(0);p=[h].concat(d.splat);j=function(){for(var a;n.length>0;)if(o=n.shift(),f.contextMatchesOptions(h,o[0])&&(a=o[1].apply(h,[h]),a===!1))return!1;f.last_route=g;h.trigger("event-context-before",{context:h});a=g.callback.apply(h,p);h.trigger("event-context-after",{context:h});return a};a.each(e.reverse(),function(a,b){var c=j;j=function(){return b.apply(h,[c])}});try{s=j()}catch(t){this.error(["500 Error",
b,c].join(" "),t)}return s}else return this.notFound(b,c)},contextMatchesOptions:function(a,b,c){if(typeof b==="undefined"||b=={})return!0;typeof c==="undefined"&&(c=!0);if(typeof b==="string"||g(b.test))b={path:b};if(b.only)return this.contextMatchesOptions(a,b.only,!0);else if(b.except)return this.contextMatchesOptions(a,b.except,!1);var d=!0,e=!0;b.path&&(d=g(b.path.test)?b.path.test(a.path):b.path.toString()===a.path);b.verb&&(e=b.verb===a.verb);return c?e&&d:!(e&&d)},getLocation:function(){return this._location_proxy.getLocation()},
setLocation:function(a){return this._location_proxy.setLocation(a)},swap:function(a){return this.$element().html(a)},templateCache:function(a,b){return typeof b!="undefined"?n[a]=b:n[a]},clearTemplateCache:function(){return n={}},notFound:function(a,b){var c=this.error(["404 Not Found",a,b].join(" "));return a==="get"?c:!0},error:function(a,b){b||(b=Error());b.message=[a,b.message].join(" ");this.trigger("error",{message:b.message,error:b});if(this.raise_errors)throw b;else this.log(b.message,b)},
_checkLocation:function(){var a,b;a=this.getLocation();if(!this.last_location||this.last_location[0]!="get"||this.last_location[1]!=a)this.last_location=["get",a],b=this.runRoute("get",a);return b},_getFormVerb:function(b){b=a(b);var c,d;d=b.find('input[name="_method"]');d.length>0&&(c=d.val());c||(c=b[0].getAttribute("method"));if(!c||c=="")c="get";return a.trim(c.toString().toLowerCase())},_checkFormSubmission:function(b){var c,d,e;this.trigger("check-form-submission",{form:b});c=a(b);d=c.attr("action");
e=this._getFormVerb(c);this.log("_checkFormSubmission",c,d,e);e==="get"?(this.setLocation(d+"?"+this._serializeFormParams(c)),b=!1):(c=a.extend({},this._parseFormParams(c)),b=this.runRoute(e,d,c,b.get(0)));return typeof b=="undefined"?!1:b},_serializeFormParams:function(a){var b="";a=a.serializeArray();var c;if(a.length>0){b=this._encodeFormPair(a[0].name,a[0].value);for(c=1;c<a.length;c++)b=b+"&"+this._encodeFormPair(a[c].name,a[c].value)}return b},_encodeFormPair:function(a,b){return j(a)+"="+j(b)},
_parseFormParams:function(a){var b={};a=a.serializeArray();var c;for(c=0;c<a.length;c++)b=this._parseParamPair(b,a[c].name,a[c].value);return b},_parseQueryString:function(a){var b={},c,d;if(a=a.match(e)){a=a[1].split("&");for(d=0;d<a.length;d++)c=a[d].split("="),b=this._parseParamPair(b,i(c[0]),i(c[1]))}return b},_parseParamPair:function(a,b,c){a[b]?h(a[b])?a[b].push(c):a[b]=[a[b],c]:a[b]=c;return a},_listen:function(a,b){return this.$element().bind([a,this.eventNamespace()].join("."),b)},_unlisten:function(a,
b){return this.$element().unbind([a,this.eventNamespace()].join("."),b)}});b.RenderContext=function(a){this.event_context=a;this.callbacks=[];this.content=this.previous_content=null;this.waiting=this.next_engine=!1};b.RenderContext.prototype=a.extend({},b.Object.prototype,{then:function(a){if(!g(a))if(typeof a==="string"&&a in this.event_context){var b=this.event_context[a];a=function(a){return b.apply(this.event_context,[a])}}else return this;var c=this;this.waiting?this.callbacks.push(a):(this.wait(),
d.setTimeout(function(){var b=a.apply(c,[c.content,c.previous_content]);b!==!1&&c.next(b)},13));return this},wait:function(){this.waiting=!0},next:function(a){this.waiting=!1;if(typeof a!=="undefined")this.previous_content=this.content,this.content=a;this.callbacks.length>0&&this.then(this.callbacks.shift())},load:function(b,c,d){var e=this;return this.then(function(){var f,h,i;g(c)?(d=c,c={}):c=a.extend({},c);d&&this.then(d);if(typeof b==="string"){f=(i=b.match(/\.json$/)||c.json)&&c.cache===!0||
c.cache!==!1;e.next_engine=e.event_context.engineFor(b);delete c.cache;delete c.json;if(c.engine)e.next_engine=c.engine,delete c.engine;if(f&&(h=this.event_context.app.templateCache(b)))return h;this.wait();a.ajax(a.extend({url:b,data:{},dataType:i?"json":null,type:"get",success:function(a){f&&e.event_context.app.templateCache(b,a);e.next(a)}},c));return!1}else{if(b.nodeType)return b.innerHTML;if(b.selector)return e.next_engine=b.attr("data-engine"),c.clone===!1?b.remove()[0].innerHTML.toString():
b[0].innerHTML.toString()}})},render:function(a,b,c){if(g(a)&&!b)return this.then(a);else{if(!b&&this.content)b=this.content;return this.load(a).interpolate(b,a).then(c)}},partial:function(a,b){return this.render(a,b).swap()},send:function(){var a=this,b=f(arguments),c=b.shift();h(b[0])&&(b=b[0]);return this.then(function(){b.push(function(b){a.next(b)});a.wait();c.apply(c,b);return!1})},collect:function(b,c,d){var e=this,f=function(){if(g(b))c=b,b=this.content;var d=[],f=!1;a.each(b,function(a,b){var g=
c.apply(e,[a,b]);g.jquery&&g.length==1&&(g=g[0],f=!0);d.push(g);return g});return f?d:d.join("")};return d?f():this.then(f)},renderEach:function(b,c,d,e){h(c)&&(e=d,d=c,c=null);return this.load(b).then(function(f){var g=this;d||(d=h(this.previous_content)?this.previous_content:[]);if(e)a.each(d,function(a,d){var h={},i=this.next_engine||b;c?h[c]=d:h=d;e(d,g.event_context.interpolate(f,h,i))});else return this.collect(d,function(a,d){var e={},g=this.next_engine||b;c?e[c]=d:e=d;return this.event_context.interpolate(f,
e,g)},!0)})},interpolate:function(a,b,c){var d=this;return this.then(function(e,f){!a&&f&&(a=f);if(this.next_engine)b=this.next_engine,this.next_engine=!1;var g=d.event_context.interpolate(e,a,b);return c?f+g:g})},swap:function(){return this.then(function(a){this.event_context.swap(a)}).trigger("changed",{})},appendTo:function(b){return this.then(function(c){a(b).append(c)}).trigger("changed",{})},prependTo:function(b){return this.then(function(c){a(b).prepend(c)}).trigger("changed",{})},replace:function(b){return this.then(function(c){a(b).html(c)}).trigger("changed",
{})},trigger:function(a,b){return this.then(function(c){typeof b=="undefined"&&(b={content:c});this.event_context.trigger(a,b)})}});b.EventContext=function(a,c,d,e,f){this.app=a;this.verb=c;this.path=d;this.params=new b.Object(e);this.target=f};b.EventContext.prototype=a.extend({},b.Object.prototype,{$element:function(){return this.app.$element(f(arguments).shift())},engineFor:function(a){var b;if(g(a))return a;a=(a||this.app.template_engine).toString();if(b=a.match(/\.([^\.]+)$/))a=b[1];if(a&&g(this[a]))return this[a];
if(this.app.template_engine)return this.engineFor(this.app.template_engine);return function(a){return a}},interpolate:function(a,b,c){return this.engineFor(c).apply(this,[a,b])},render:function(a,c,d){return(new b.RenderContext(this)).render(a,c,d)},renderEach:function(a,c,d,e){return(new b.RenderContext(this)).renderEach(a,c,d,e)},load:function(a,c,d){return(new b.RenderContext(this)).load(a,c,d)},partial:function(a,c){return(new b.RenderContext(this)).partial(a,c)},send:function(){var a=new b.RenderContext(this);
return a.send.apply(a,arguments)},redirect:function(){var a;a=f(arguments);var b=this.app.getLocation();a.length>1?(a.unshift("/"),a=this.join.apply(this,a)):a=a[0];this.trigger("redirect",{to:a});this.app.last_location=[this.verb,this.path];this.app.setLocation(a);b==a&&this.app.trigger("location-changed")},trigger:function(a,b){typeof b=="undefined"&&(b={});if(!b.context)b.context=this;return this.app.trigger(a,b)},eventNamespace:function(){return this.app.eventNamespace()},swap:function(a){return this.app.swap(a)},
notFound:function(){return this.app.notFound(this.verb,this.path)},json:function(b){return a.parseJSON(b)},toString:function(){return"Sammy.EventContext: "+[this.verb,this.path,this.params].join(" ")}});a.sammy=d.Sammy=b})(jQuery,window);Sammy.HashPushProxy=function(a,d){this.app=a;this.supportsHistory=!(!window.history||!history.pushState);if(!this.supportsHistory)this._startPolling(d),this.is_native=!1};
Sammy.HashPushProxy.prototype={bind:function(){var a=this,d=this.app;if(this.app.supportsHistory)$(window).bind("popstate",function(){a.app.trigger("location-changed")}),$("a").live("click",function(b){location.hostname==this.hostname&&(b.preventDefault(),a.historyAPISupported?a.setLocation($(this).attr("href")):a.setLocation("#"+$(this).attr("href")),a.app.trigger("location-changed"))});else{$(window).bind("hashchange."+this.app.eventNamespace(),function(b,c){if(a.is_native===!1&&!c)Sammy.log("native hash change exists, using"),
a.is_native=!0,window.clearInterval(Sammy.HashLocationProxy._interval);d.trigger("location-changed")});if(!Sammy.HashLocationProxy._bindings)Sammy.HashLocationProxy._bindings=0;Sammy.HashLocationProxy._bindings++}},unbind:function(){this.app.supportsHistory?($("a").unbind("click"),$(window).unbind("popstate")):($(window).unbind("hashchange."+this.app.eventNamespace()),Sammy.HashLocationProxy._bindings--,Sammy.HashLocationProxy._bindings<=0&&window.clearInterval(Sammy.HashLocationProxy._interval))},
getLocation:function(){if(this.app.supportsHistory)return window.location.pathname;else{var a=window.location.toString().match(/^[^#]*(#.+)$/);return a?a[1]:""}},setLocation:function(a){if(this.app.supportsHistory)history.pushState({path:this.path},"",a);else return window.location=a},_startPolling:function(a){var d=this;if(!Sammy.HashLocationProxy._interval){a||(a=10);var b=function(){var a=d.getLocation();(!Sammy.HashLocationProxy._last_location||a!=Sammy.HashLocationProxy._last_location)&&window.setTimeout(function(){$(window).trigger("hashchange",
[!0])},13);Sammy.HashLocationProxy._last_location=a};b();Sammy.HashLocationProxy._interval=window.setInterval(b,a)}}};
(function(a){var d={};Sammy=Sammy||{};Sammy.Template=function(b,c){c||(c="template");b.helper(c,function(b,c,g,h){typeof g=="undefined"&&(g=b);typeof h=="undefined"&&typeof g=="object"&&(h=g,g=b);a:{c=a.extend({},this,c);if(d[g])b=d[g];else{if(typeof b=="undefined"){b=!1;break a}h=h&&h.escape_html===!1?'",$1,"':'",h($1),"';b=d[g]=new Function("obj",'var ___$$$___=[],print=function(){___$$$___.push.apply(___$$$___,arguments);};with(obj){___$$$___.push("'+String(b).replace(/[\r\t\n]/g," ").replace(/\"/g,
'\\"').split("<%").join("\t").replace(/((^|%>)[^\t]*)/g,"$1\r").replace(/\t=(.*?)%>/g,h).replace(/\t!(.*?)%>/g,'",$1,"').split("\t").join('");').split("%>").join('___$$$___.push("').split("\r").join("")+"\");}return ___$$$___.join('');")}b=typeof c!="undefined"?b(c):b}return b})}})(jQuery);

52
ffdemo/static/assets/js/vendor/canvas_extensions.js поставляемый Normal file
Просмотреть файл

@ -0,0 +1,52 @@
CanvasRenderingContext2D.prototype.dashedLineTo = function( fromX, fromY, toX, toY, pattern ) {
// Our growth rate for our line can be one of the following:
// (+,+), (+,-), (-,+), (-,-)
// Because of this, our algorithm needs to understand if the x-coord and
// y-coord should be getting smaller or larger and properly cap the values
// based on (x,y).
var lt = function ( a, b ) { return a <= b; };
var gt = function ( a, b ) { return a >= b; };
var capmin = function ( a, b ) { return Math.min( a, b ); };
var capmax = function ( a, b ) { return Math.max( a, b ); };
// if ( typeof(pattern) != "Array" ) pattern = [pattern];
var checkX = { thereYet: gt, cap: capmin };
var checkY = { thereYet: gt, cap: capmin };
if ( fromY - toY > 0 ) {
checkY.thereYet = lt;
checkY.cap = capmax;
}
if ( fromX - toX > 0 ) {
checkX.thereYet = lt;
checkX.cap = capmax;
}
this.moveTo( fromX, fromY );
var offsetX = fromX;
var offsetY = fromY;
var idx = 0, dash = true;
while ( !( checkX.thereYet( offsetX, toX ) && checkY.thereYet( offsetY, toY ) ) ) {
var ang = Math.atan2( toY - fromY, toX - fromX );
var len = pattern[idx];
offsetX = checkX.cap( toX, offsetX + (Math.cos( ang ) * len ) );
offsetY = checkY.cap( toY, offsetY + (Math.sin( ang ) * len ) );
if ( dash ) this.lineTo( offsetX, offsetY );
else this.moveTo( offsetX, offsetY );
idx = ( idx + 1 ) % pattern.length;
dash = !dash;
}
};
CanvasRenderingContext2D.prototype.dottedArc = function( x, y, radius, startAngle, endAngle, anticlockwise ) {
var g = Math.PI / radius / 2, sa = startAngle, ea = startAngle + g;
while( ea < endAngle ) {
this.beginPath();
this.arc( x, y, radius, sa, ea, anticlockwise );
this.stroke();
sa = ea + g;
ea = sa + g;
}
};

1
ffdemo/static/assets/js/vendor/country_codes.json поставляемый Normal file

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

102
ffdemo/static/assets/js/vendor/jquery.collapsibleMod.js поставляемый Normal file
Просмотреть файл

@ -0,0 +1,102 @@
/*
* makes divs with an h3 and a ul collapse. Not very useful for other things.
*
*/
(function($) {
$.collapsibleMod = {
cfg: {
'collapsedClass': 'collapsibleMod-collapsed',
'expandedClass': 'collapsibleMod-expanded',
'$header': null,
'expandedHeight': 0,
'collapsedHeight': 0,
'$content': null,
'collapsed': false,
'stateKey': '',
'saveState': true // when set to true, and the container ele has an id, we'll attempt to save the state between requests
},
fn: {
'init': function ( container, options ) {
var $this = $( container );
var context = $.extend({}, $.collapsibleMod.cfg, options );
context.$container = $this;
context.$header = $this.find( 'h3:first' );
context.$content = $this.children().not( 'h3:first' );
// save our height to expand to
context.expandedHeight = context.$content.height();
context.$header.bind( 'click', function( e ) {
e.preventDefault();
$.collapsibleMod.fn.toggle( context );
} );
// setup state saving
if( context.saveState && context.$container.attr( 'id' ) != "" && typeof localStorage != 'undefined' ) {
context.stateKey = 'collapsibleMod-state-' + context.$container.attr( 'id' );
$.collapsibleMod.fn.restoreState( context );
} else {
context.saveState = false;
}
if ( context.collapsed ) {
$.collapsibleMod.fn.collapse( context );
} else {
context.$container
.addClass( context.expandedClass );
}
$this.data( 'collapsibleMod-context', context );
},
'collapse': function ( context ) {
context.$container
.addClass( context.collapsedClass )
.removeClass( context.expandedClass );
context.$content
.animate( { 'height': context.collapsedHeight }, 'fast', function() {
if ( context.collapsedHeight == 0 )
context.$content.hide();
} );
context.collapsed = true;
$.collapsibleMod.fn.saveState( context );
},
'expand': function ( context ) {
context.$container
.removeClass( context.collapsedClass )
.addClass( context.expandedClass );
context.$content
.show()
.animate( { 'height': context.expandedHeight }, 'fast' );
context.collapsed = false;
$.collapsibleMod.fn.saveState( context );
},
'saveState': function( context ) {
if( context.saveState ) {
try {
localStorage.removeItem( context.stateKey );
localStorage.setItem( context.stateKey, context.collapsed );
} catch (e) {
if ( e == QUOTA_EXCEEDED_ERR ) { /* data wasn't successfully saved due to quota exceed */ }
}
}
},
'restoreState': function( context ) {
if ( context.saveState && localStorage.getItem( context.stateKey ) ) {
context.collapsed = ( localStorage.getItem( context.stateKey ) === 'true' ) ;
}
},
'toggle': function ( context ) {
context.collapsed ? $.collapsibleMod.fn.expand( context ) : $.collapsibleMod.fn.collapse( context );
}
}
};
$.fn.collapsibleMod = function ( options ) {
return $( this ).each( function () {
$.collapsibleMod.fn.init( this, options );
} );
};
})(jQuery);

68
ffdemo/static/assets/js/vendor/jquery.delayedBind.js поставляемый Normal file
Просмотреть файл

@ -0,0 +1,68 @@
(function( $ ) {
/**
* Function that escapes spaces in event names. This is needed because
* "_delayedBind-foo bar-1000" refers to two events
*/
function encodeEvent( event ) {
return event.replace( /-/g, '--' ).replace( / /g, '-' );
}
$.fn.extend( {
/**
* Bind a callback to an event in a delayed fashion.
* In detail, this means that the callback will be called a certain
* time after the event fires, but the timer is reset every time
* the event fires.
* @param timeout Number of milliseconds to wait
* @param event Name of the event (string)
* @param data Data to pass to the event handler (optional)
* @param callback Function to call
*/
delayedBind: function( timeout, event, data, callback ) {
var encEvent = encodeEvent( event );
return this.each( function() {
var that = this;
// Bind the top half
// Do this only once for every (event, timeout) pair
if ( !( $(this).data( '_delayedBindBound-' + encEvent + '-' + timeout ) ) ) {
$(this).data( '_delayedBindBound-' + encEvent + '-' + timeout, true );
$(this).bind( event, function() {
var timerID = $(this).data( '_delayedBindTimerID-' + encEvent + '-' + timeout );
// Cancel the running timer
if ( typeof timerID != 'undefined' )
clearTimeout( timerID );
timerID = setTimeout( function() {
$(that).trigger( '_delayedBind-' + encEvent + '-' + timeout );
}, timeout );
$(this).data( '_delayedBindTimerID-' + encEvent + '-' + timeout, timerID );
} );
}
// Bottom half
$(this).bind( '_delayedBind-' + encEvent + '-' + timeout, data, callback );
} );
},
/**
* Cancel the timers for delayed events on the selected elements.
*/
delayedBindCancel: function( timeout, event ) {
var encEvent = encodeEvent( event );
return this.each( function() {
var timerID = $(this).data( '_delayedBindTimerID-' + encEvent + '-' + timeout );
if ( typeof timerID != 'undefined' )
clearTimeout( timerID );
} );
},
/**
* Unbind an event bound with delayedBind()
*/
delayedBindUnbind: function( timeout, event, callback ) {
var encEvent = encodeEvent( event );
return this.each( function() {
$(this).unbind( '_delayedBind-' + encEvent + '-' + timeout, callback );
} );
}
} );
} )( jQuery );

526
ffdemo/static/assets/js/vendor/jquery.markApp.capture.js поставляемый Normal file
Просмотреть файл

@ -0,0 +1,526 @@
( function( $ ) {
var markApp = $.markApp = $.markApp || {};
var modules = $.markApp.modules = $.markApp.modules || {};
modules.capture = {
defaults: {
state: 'intro', // possible values -> intro, drawing, submitting
invite_code: null,
locale: null,
contributor_type: null
},
config: {
captureLimit: 300,
layerManager: null,
capturedPoints: 0,
strokes: [],
framecount: 0,
cleanedStrokes: [],
lastX: null,
lastY: null,
captureTime: null,
rtl: null,
initialized: false,
currentStroke: null,
mark: null,
timeBetweenStrokes: 400, // the amount of time to pause between strokes
events: [] // unexecuted events we bind to times to
},
// Event handlers
evt: {
resize: function ( context, e ) {
var lC = context.modules.capture;
lC.layerManager.resizeAll( context.width, context.height );
// redraw all marks
if( lC.mark && lC.mark.strokes.length > 0 ) {
for( var i = 0; i < lC.mark.strokes.length; i++ ) {
modules.capture.fn.drawStroke( context, lC.mark.strokes[i] );
}
}
},
mousemove: function( context, e ) {
if ( context.mouseDown && context.modules.capture.state == "drawing" )
modules.capture.fn.capturePoint( context );
},
mousedown: function( context, e ) {
switch ( context.modules.capture.state ) {
case "drawing":
// close the country select if it's open
if( $( '#location-dialog' ).is( ':visible' ) ) {
$( '#location-dialog' )
.fadeOut( 'fast' );
}
// start a new mark if we need to
if( !context.modules.capture.mark ) modules.capture.fn.startMark( context );
// start a new stroke unless we already have a stroke open for some reason
if( !context.modules.capture.currentStroke ) modules.capture.fn.startStroke( context );
break;
case "intro":
// start a new mark if we need to
if( !context.modules.capture.mark ) modules.capture.fn.startMark( context );
// start a new stroke unless we already have a stroke open for some reason
if( !context.modules.capture.currentStroke ) modules.capture.fn.startStroke( context );
modules.capture.fn.endIntro( context );
break;
}
},
mouseup: function( context, e ) {
if( context.modules.capture.state == "drawing" ) {
modules.capture.fn.endStroke( context );
}
},
ready: function ( context, e ) {
var lC = context.modules.capture;
// hide errything
$( '#markmaker' )
.hide()
.children()
.hide();
// template dom is ready
$( '#markmaker-reset a' )
.addClass( 'disabled' )
.bind( 'mousedown', function( e ) {
e.preventDefault();
modules.capture.fn.reset( context );
} );
$( '#markmaker-submit a' )
.addClass( 'disabled' )
.bind( 'mousedown', function( e ) {
e.preventDefault();
modules.capture.fn.submit( context );
} );
// load the country codes into the dialog
context.fn.withCountryCodes( function ( countryCodes ) {
var $select = $( '#markmaker-country' );
for( var i = 0; i < countryCodes.length; i++ ) {
var $option = $( '<option />' )
.val( countryCodes[i].code )
.text( countryCodes[i].name );
$select.append( $option );
}
} );
$( '#markmaker-location a' )
.bind( 'mousedown', {context: context}, modules.capture.fn.locationDialogToggle );
$( '#markmaker-information' )
.bind( 'mouseover', {context: context}, modules.capture.fn.informationDialogToggle )
.bind( 'mouseout', {context: context}, modules.capture.fn.informationDialogToggle );
if ( lC.state == "drawing" ) {
modules.capture.fn.initDrawing( context );
}
$("#sammy #markmaker-country").selectBox({ autoWidth: false });
},
loop: function ( context, e ) {
var lC = context.modules.capture;
if ( !lC.initialized ) return;
// increment the frame counter
lC.frameCount++;
// Draw the cursor
modules.capture.fn.commonLoop( context );
// state specific code
switch( context.modules.capture.state ) {
case "drawing":
modules.capture.fn.drawLoop( context );
break;
}
}
},
fn: {
init: function ( context, options ) {
var lC = context.modules.capture;
// if we've already set the interface up, just reset it
if ( '$capture' in lC ) {
// FIXME -- this reinit portion could use some love
if( 'state' in options ) {
modules.capture.fn.reset( context );
if ( options['state'] == 'drawing' ) {
if( context.mouseDown )
context.fn.trigger( 'mousedown' );
}
lC.state = options['state'];
modules.capture.fn.initDrawing( context );
}
} else {
// allow defaults to be overriden
$.extend( lC, modules.capture.defaults );
$.extend( lC, options );
// but not the cofig
$.extend( lC, modules.capture.config );
// DOM setup
lC.$capture = $( '<div />' )
.addClass( "capture-container" );
context.$container
.css( { 'zIndex': 100, 'cursor': 'none' } )
.append( lC.$capture );
lC.layerManager = new Mark.layerManager( lC.$capture.get( 0 ) );
// add two layers for the interface to use
lC.layerManager.addLayer( 'drawnLayer' );
lC.layerManager.addLayer( 'liveDrawingLayer' );
// trigger resize so our new layers are sized to fit
context.fn.trigger( 'resize' );
lC.initialized = true;
}
},
deinit: function( context ) {
var lC = context.modules.capture;
// fade out our container
lC.$capture.fadeOut( 'fast', function () {
// remove all our layers
lC.layerManager.removeAll();
lC.$capture.remove();
lC.initialized = false;
} );
},
initIntro: function ( context ) {
},
initDrawing: function ( context ) {
var lC = context.modules.capture;
// hide any intro stuff that might be being displayed
if( $( '#browse-marks' ).is( ':visible' ) ) {
$( '#browse-marks, #click-anywhere, #intro-main-copy' )
.fadeOut( 'fast' );
$( '#markmaker-legal-line' ).fadeIn( 'slow' );
}
$( '#markmaker' ).css( 'background-position', '0 ' + ( context.height - 140 ) + 'px' );
// update our resize handler
$( '#markmaker' )
.unbind( 'resize.markApp' )
.bind( 'resize.markApp', function ( e, w, h ) {
$( '#markmaker' ).css( 'background-position', '0 ' + ( context.height - 140 ) + 'px' );
// if there are dialogs open, reposition them
if( $( '#location-dialog:visible').size() > 0 ) {
$( '#location-dialog:visible' )
.css( {
'bottom': $( '#markmaker-location' ).height() + 25,
'left': $( '#markmaker-location' ).offset().left + 32
} );
}
} );
if( !$( '#markmaker' ).is( ':visible' ) ) {
$( '#markmaker' )
.width( 0 )
.show()
.animate( { 'width': context.width }, 'slow', function () {
$( '#markmaker-controls' ).fadeIn( 'slow', function() {
$( '#markmaker-information' ).fadeIn( 'slow' );
} );
$( '#markmaker-legal-line' ).fadeIn( 'slow' );
} );
} else {
$( '#markmaker-controls' ).fadeIn( 'slow', function() {
$( '#markmaker-information' ).fadeIn( 'slow' );
} );
}
// special cases
if ( lC.invite_code && lC.contributor_type == "t" ) {
lC.captureLimit = 1000;
lC.$capture.addClass( 'translator' );
$( '#translator-fields' )
.find( '#translator-locale' )
.text( "'" + context.locale + "'" )
.end()
.collapsibleMod( )
.fadeIn( 'slow' );
} else if ( lC.invite_code && lC.contributor_type == "c" ) {
lC.$capture.addClass( 'contributor' );
$( '#contributor-fields' )
.collapsibleMod( )
.fadeIn( 'slow' );
}
},
// fades out the intro content and switches into drawing mode
endIntro: function ( context ) {
var lC = context.modules.capture;
// switch to drawing mode
context.app.setLocation( '#/mark/new' );
},
locationDialogToggle: function ( e, context ) {
e.preventDefault();
if( $( '#location-dialog' ).is( ':visible' ) ) {
$( '#location-dialog' )
.fadeOut( 'fast' );
} else {
$( '#location-dialog' )
.fadeIn( 'fast' )
.css( {
'bottom': $( '#markmaker-location' ).height() + 25,
'left': $( '#markmaker-location' ).offset().left + 32
} );
}
},
informationDialogToggle: function ( e, context ) {
e.preventDefault();
if( e.type == "mouseout") {
$( '#information-dialog' )
.fadeOut( 'fast' );
} else {
$( '#information-dialog' )
.fadeIn( 'fast' )
.css( {
'bottom': $( '#markmaker-information' ).height() + 36,
'left': $( '#markmaker-information' ).offset().left - $( '#information-dialog' ).width() + 30
} );
}
},
startMark: function ( context ) {
var lC = context.modules.capture;
// setup the mark
lC.captureTime = ( new Date() ).getTime();
lC.rtl = context.mouseX > $( window ).width() / 2;
lC.mark = new Mark.gmlMark( [], '', '', lC.captureTime, lC.rtl );
// remove the disabled styling from the submit and reset buttons
$( '#markmaker-submit a, #markmaker-reset a' ).removeClass( 'disabled' );
},
endMark: function ( context ) {
var lC = context.modules.capture;
// close out the mark and prep for submission
lC.mark.setupVars();
},
startStroke: function ( context ) {
// start a new, empty stroke
var lC = context.modules.capture;
lC.currentStroke = [];
// set the time relative to the last stroke, plus an offset
if( lC.strokes.length > 0 )
lC.captureTime = ( new Date() ).getTime() -
( lC.strokes[lC.strokes.length - 1][lC.strokes[lC.strokes.length - 1].length - 1].time + lC.timeBetweenStrokes );
},
endStroke: function ( context ) {
var lC = context.modules.capture;
// ignore strokes with less than three points
if ( lC.currentStroke.length > 2 ) {
// close out this stroke
lC.strokes.push( lC.currentStroke );
// run the simplification algorithim
var simpStroke = Mark.simplification.simplifyPath( lC.currentStroke, 1 );
// run the weighting algorithm
simpStroke = Mark.simplification.weightPath( simpStroke, [5,10,20,40] );
lC.mark.strokes.push( simpStroke );
lC.cleanedStrokes.push( simpStroke );
// draw this stroke the to drawn layer
modules.capture.fn.drawStroke( context, simpStroke );
// recalculate the captured point count
lC.capturedPoints -= lC.currentStroke.length - simpStroke.length;
}
// set the currentStroke to null
lC.currentStroke = null;
},
capturePoint: function ( context ) {
var lC = context.modules.capture;
if( lC.capturedPoints > lC.captureLimit ) {
context.fn.trigger( 'mouseup' );
modules.capture.fn.closeShop( context );
return;
}
var time = ( new Date() ).getTime();
// create a new point and add it to the current stroke
var point = new Mark.gmlPoint( context.mouseX, context.mouseY, time - lC.captureTime, 0 );
if( lC.currentStroke.length > 0 ) {
var lastPoint = lC.currentStroke[lC.currentStroke.length - 1];
point.speed = lastPoint.speedToPoint( point );
point.setAngleFromPoint( lastPoint );
point.smoothAgainst( lastPoint, 1/100 );
} else {
// if this isn't the first stroke, draw a connecting line
if( lC.strokes.length >= 1 ) {
modules.capture.fn.drawGuide( lC.layerManager.layers['drawnLayer'].context, lC.lastX, lC.lastY, point.x, point.y );
}
}
lC.currentStroke.push( point );
lC.lastX = point.x;
lC.lastY = point.y;
// increment our total points counter
lC.capturedPoints++;
},
reset: function ( context ) {
var lC = context.modules.capture;
lC.layerManager.layers['liveDrawingLayer'].clean();
lC.layerManager.layers['drawnLayer'].clean();
lC.capturedPoints = 0;
lC.rtl = null;
lC.mouseDown = false
lC.lastX = null;
lC.lastY = null;
lC.strokes = [];
lC.currentStroke = null;
lC.mark = null;
lC.captureTime = null;
lC.state = "drawing";
$( '#markmaker-submit a, #markmaker-reset a' ).addClass( 'disabled' );
$( '#markapp' ).css( { 'cursor': 'none' } );
$( '#markmaker-instructions' ).fadeIn();
},
closeShop: function( context ) {
var lC = context.modules.capture;
lC.state = 'preview';
// close the mark
modules.capture.fn.endMark( context );
// clear the drawing layer
lC.layerManager.layers['liveDrawingLayer'].clean();
// if the user is out of points, draw the line to the opposite side of the screen
var g = lC.layerManager.layers['liveDrawingLayer'].context,
x = lC.lastX,
y = lC.lastY;
g.strokeStyle = 'rgba(0,0,0,0.2)';
g.lineWidth = 1;
g.beginPath();
g.dashedLineTo( x, y, lC.rtl ? 0 : lC.layerManager.layers['liveDrawingLayer'].canvas.width, y, [7, 5] );
g.closePath();
g.stroke();
// set our cursor back to normal
$( '#markapp' ).css( { 'cursor': 'default' } );
},
submit: function( context ) {
var lC = context.modules.capture;
if( lC.state == "submitting" ) return;
if( lC.state != "preview" ) modules.capture.fn.closeShop( context );
// process our points, and send them off
lC.state = "submitting";
$( '#markmaker-submit a' ).addClass( 'disabled' );
var data = {};
data.rtl = lC.rtl;
data.strokes = lC.strokes;
//data.locale = cfg.locale; // TODO - impliment location awareness
var points_obj = JSON.stringify( data );
var points_obj_simplified = JSON.stringify( lC.mark );
var country_code = $( '#markmaker-country' ).val() == "label" ? "" : $( '#markmaker-country' ).val();
// show loader
context.fn.showLoader( context.fn.getString( 'submitting-mark' ) );
var params = {
'points_obj': points_obj,
'points_obj_simplified': points_obj_simplified,
'country_code': country_code
};
if ( lC.invite_code && lC.contributor_type == "t" ) {
params.contributor_locale = context.locale;
params.invite = lC.invite_code;
} else if (lC.invite_code && lC.contributor_type == "c" ) {
params.contributor = $( '#contributor-name' ).val();
// add the quote as the marks extra_info
lC.mark.extra_info = $( '#contributor-quote' ).val();
// re-stringify our points obj
params.points_obj_simplified = JSON.stringify( lC.mark );
params.invite = lC.invite_code;
}
$.ajax( {
url: '/requests/save_mark',
data: params,
type: 'POST',
dataType: 'JSON',
success: function( data ) {
// if we submitted a locale, the mark wont be in the line, so just start at the beginning
if ( params.contributor_locale ) {
context.app.setLocation( '#/linear/' );
context.fn.hideLoader();
} else {
// store the users mark for later access
context.fn.storeData( 'userMark', { 'reference': data.mark_reference, 'country_code': country_code } );
// now tell the app to redirect to our FRESH mark
context.app.setLocation( '#/linear/'+ data.mark_reference + '?playback=true' );
// hide loader
context.fn.hideLoader();
}
},
error: function( data ) {
context.fn.showError( context.fn.getString( 'submit-error' ) );
}
} );
return false;
},
drawStroke: function ( context, stroke ) {
var lC = context.modules.capture;
Mark.thickBrush( lC.layerManager.layers['drawnLayer'].context, [stroke] );
lC.layerManager.layers['drawnLayer'].context.fillStyle = "rgba(255,255,255,0.3)";
lC.layerManager.layers['drawnLayer'].context.strokeStyle = "rgba(255,255,255,0.3)";
Mark.circleBrush( lC.layerManager.layers['drawnLayer'].context, [stroke] );
},
drawGuide: function( g, x1, y1, x2, y2 ) {
g.strokeStyle = 'rgba(0,0,0,0.2)';
g.lineWidth = 1;
g.beginPath();
g.dashedLineTo( x1, y1, x2, y2, [7, 5] );
g.closePath();
g.stroke();
},
drawCursor: function ( g, x, y, per ) {
g.strokeStyle = '#ff5400';
g.fillStyle = '#000000';
// draw stroke
g.beginPath();
g.moveTo( x, y );
g.lineTo( x + 1, y - 8 );
g.lineTo( x + 20, y - 27 );
g.lineTo( x + 23, y - 23 );
g.lineTo( x, y );
g.closePath();
g.stroke();
// draw filling
per *= 18.5;
per += 4.5;
g.beginPath();
g.moveTo( x, y );
g.lineTo( x + 1, y - 8 );
g.lineTo( x + ( per - 3 ), y - ( per + 4 ) );
g.lineTo( x + per, y - per );
g.lineTo( x, y );
g.closePath();
g.fill();
},
commonLoop: function( context ) {
var lC = context.modules.capture;
// clear the drawing layer
lC.layerManager.layers['liveDrawingLayer'].clean();
// draw the cursor if the cursor is in the frame
if( context.mouseIn && ( lC.state == "drawing" || lC.state == "intro" ) ) {
modules.capture.fn.drawCursor( lC.layerManager.layers['liveDrawingLayer'].context, context.mouseX, context.mouseY, ( lC.captureLimit - lC.capturedPoints ) / lC.captureLimit );
}
},
introLoop: function( context ) {
},
drawLoop: function( context ) {
var lC = context.modules.capture;
// Clean the drawing layer
if( lC.currentStroke && lC.currentStroke.length > 0 ) {
// draw out what we've got in the stroke buffer
Mark.thickBrush( lC.layerManager.layers['liveDrawingLayer'].context, [lC.currentStroke] );
lC.layerManager.layers['liveDrawingLayer'].context.fillStyle = "rgba(255,255,255,0.3)";
lC.layerManager.layers['liveDrawingLayer'].context.strokeStyle = "rgba(255,255,255,0.3)";
Mark.circleBrush( lC.layerManager.layers['liveDrawingLayer'].context, [lC.currentStroke] );
}
if( ! context.mouseIn ) return;
if( ! context.mouseDown ) {
// draw the guide
var x, y;
if( lC.strokes.length == 0 ) {
x = context.mouseX > $( window ).width() / 2 ? lC.layerManager.layers['liveDrawingLayer'].canvas.width : 0,
y = context.mouseY;
} else {
x = lC.lastX;
y = lC.lastY;
}
modules.capture.fn.drawGuide( lC.layerManager.layers['liveDrawingLayer'].context, x, y, context.mouseX, context.mouseY );
}
}
}
};
}( jQuery ) );

310
ffdemo/static/assets/js/vendor/jquery.markApp.intro.js сгенерированный поставляемый Normal file
Просмотреть файл

@ -0,0 +1,310 @@
( function( $ ) {
// support loose augmentation
markApp = $.markApp = $.markApp || {};
modules = $.markApp.modules = $.markApp.modules || {};
// Code for introducing the site.
// runs quickly, then unloads
// store all data in a flat json file that django recompiles after a new intro mark is added
modules.intro = {
defaults: {
// optional reference mark -- will init the visualization on this mark if passed
reference_mark: null
},
config: {
// all marks held here
marks: {},
animationMarks: ['vVR', 'myWb'],
playbackTimes: {},
vizScene: null, // for rendering the viz preview
textScene: null, // for rendering tahe extra text
// layer manager
layerManager: null,
// boolean flag to indicate if this module is fully setup
initialized: false,
eventChange: false,
curLocaleMark: null,
animationComplete: false,
tweens: {}
},
evt: {
resize: function( context, e ) {
context.modules.intro.layerManager.resizeAll( context.width, context.height );
context.modules.intro.eventChange = true;
if ( context.modules.intro.xAnimationComplete ) {
// redraw the X
modules.intro.fn.drawX( context );
// reposition and redraw the translateMark
}
},
loop: function ( context, e ) {
var lC = context.modules.intro;
// update the position of the camera and draw the viz preview
TWEEN.update();
lC.layerManager.layers['viz'].clean();
Mark.renderer.renderScene( lC.vizScene, { width: context.width, height: context.height } );
if( lC.curLocaleMark || lC.xMark ) {
lC.layerManager.layers['mainMark'].clean();
// render the locale mark
if( lC.curLocaleMark ) {
var scale = 500 / lC.curLocaleMark.bWidth;
Mark.renderer.renderMark(
lC.layerManager.layers['mainMark'].context,
lC.curLocaleMark,
{ offset: {x: (context.width / 2) - 115, y: context.height - 240 },
scale: {x: scale, y: scale, thickness: scale},
color: '255,84,0',
timer: lC.textScene.timers[lC.curLocaleMark.reference] } );
}
if ( lC.xMark ) {
// RENDER THE X
var xScale = ( ( ( context.width / 2 ) - 200 ) / lC.xMark.bWidth );
var yScale = ( ( context.height + 200 ) / lC.xMark.bHeight );
Mark.renderer.renderMark(
lC.layerManager.layers['mainMark'].context,
lC.xMark,
{ offset: {x: 10, y: -100 },
scale: {x: xScale, y: yScale, thickness: 5},
color: '0,0,0',
timer: lC.textScene.timers[lC.xMark.reference] } );
}
}
},
ready: function( context, e ) {
$( '#markmaker' )
.hide()
.children()
.hide();
/*
* Here's the order in which we want to run this intro
* 1. quick viz preview with make your mark in varying languages (2 sec)
* 2. dotted line animated across the screen as the viz preview fades out ( 1 sec)
* 3. big X draws in (2 sec)
* 4. dom elements animate up (1 sec)
* 5. Make your Mark draws in (4 sec)
*
* If any part of this fails, we revert to a simple intro where our intro screens if faded in.
*
*/
$.when( modules.intro.fn.initInterface( context ), modules.intro.fn.loadMarks( context ) )
.then( function() { modules.intro.fn.runVizPreview( context ); } )
.then( function() { modules.intro.fn.startDomAnimation( context ); } )
.fail( function() { modules.intro.fn.simpleIntro( context ); } );
}
},
fn: {
init: function( context, options ) {
var lC = context.modules.intro;
if ( lC.initialized ) {
// this module isn't really intended to be reloaded
// now our options into our context
$.extend( lC, lC, options );
// since merging won't replace null or undefined values, make sure we clean up after it
for( option in modules.intro.defaults ) {
if ( options[option] == null ) lC[option] = modules.intro.defaults[option];
}
} else {
// allow defaults to be overriden
$.extend( lC, modules.intro.defaults, options );
// but not the cofig
$.extend( lC, lC, modules.intro.config );
// DOM setup
lC.$intro = $( '<div />' )
.addClass( "intro-container" );
context.$container
.append( lC.$intro );
// scene setup
lC.vizScene = new Mark.scene();
lC.textScene = new Mark.scene();
// layer setup
lC.layerManager = new Mark.layerManager( lC.$intro.get( 0 ) );
// we draw the viz preview to this
lC.layerManager.addLayer( 'viz' );
lC.vizScene.canvasContext = lC.layerManager.layers['viz'].context;
// and then the 'Make Your Mark' on this
lC.layerManager.addLayer( 'mainMark' );
lC.textScene.canvasContext = lC.layerManager.layers['mainMark'].context;
// and for the X
lC.layerManager.addLayer( 'X' );
// trigger resize so our new layers are sized to fit
context.fn.trigger( 'resize' );
lC.initialized = true;
}
},
deinit: function( context ) {
var lC = context.modules.intro;
lC.$intro.fadeOut( 'fast', function () {
// remove all our layers
lC.layerManager.removeAll();
lC.$intro.remove();
lC.initialized = false;
} );
},
initInterface: function ( context ) {
$( '#markmaker' )
.unbind( 'resize.markApp' )
.bind( 'resize.markApp', function ( e, w, h ) {
// reposition the elements
var rAnchor = ( w / 2 ) + 485;
var bOffset = ( h - 140 ); // position of the background graphic
var wasHidden = false;
$( '#markmaker' ).css( 'background-position', '0 ' + bOffset + 'px' );
// if these aren't shown yet, do this quick trick
if( !$( '#markmaker' ).is( ':visible' ) ) {
wasHidden = true;
$( '#markmaker, #browse-marks, #click-anywhere, #intro-main-copy' )
.css( { 'display': 'block' } );
}
$( '#browse-marks' )
.css( { 'top': bOffset - 50, 'left': rAnchor - 85 } );
$( '#click-anywhere' )
.css( { 'top': bOffset + 12, 'left': rAnchor - $( '#intro-main-copy' ).width() } );
$( '#intro-main-copy' )
.css( { 'top': bOffset - $( '#intro-main-copy' ).height() - 100, 'left': rAnchor - $( '#intro-main-copy' ).width() } );
if( wasHidden ) {
$( '#markmaker, #browse-marks, #click-anywhere, #intro-main-copy' )
.css( { 'display': 'none' } );
}
} )
.trigger( 'resize.markApp', [context.width, context.height] )
.width( 0 )
.height( context.height );
},
loadMarks: function( context ) {
return $.ajax( {
url: '/requests/get_translated_marks',
dataType: 'JSON'
} )
.success( function ( data ) {
modules.intro.fn.setupMarks( context, data.marks );
} );
},
setupMarks: function( context, marks ) {
var lC = context.modules.intro;
// FXIME - if this is empty, throw an error
if( typeof marks === "undefined" || marks.length == 0 ) return;
// set our layer's visiblity to 0
$( lC.layerManager.layers['viz'].canvas ).css( 'opacity', 0 );
// sort the marks randomly
marks.sort( function( a, b ) { return ( Math.round( Math.random() ) - 0.5 ); } );
// // duplicate marks until we've got 25
// while ( marks.length < 25 ) {
// marks = marks.concat( marks );
// }
var pMark = null;
// add them to the scene
for ( var i = 0; i < marks.length; i++ ) {
try {
var points_obj = JSON.parse( marks[i].points_obj_simplified );
var mark = new Mark.gmlMark( points_obj.strokes, marks[i].reference, marks[i].country_code, marks[i].date_drawn, points_obj.rtl, marks[i].id, marks[i].is_approved );
if ( !lC.currentMark ) lC.currentMark = mark;
// if we dont have a permanant ref to this mark yet, create it
if( !( marks[i].reference in lC.marks ) ) {
lC.marks[mark.reference] = mark;
}
// position this mark relative to the last one
if ( pMark ) mark.positionRelativeTo( pMark, false );
if ( marks[i].contributor_locale == context.locale || !lC.curLocaleMark ) {
var distantFuture = ( new Date() ).getTime() * 3;
lC.curLocaleMark = new Mark.gmlMark( points_obj.strokes, marks[i].reference, marks[i].country_code, marks[i].date_drawn, points_obj.rtl, marks[i].id, marks[i].is_approved );
lC.textScene.timers[lC.curLocaleMark.reference] = { 'start': distantFuture, 'end': distantFuture + lC.curLocaleMark.maxTime, 'speed': 2 };
}
lC.vizScene.objects.push( mark );
pMark = mark;
} catch ( e ) {
// console.warn( "Mark failed import", marks[i].reference );
}
}
},
runVizPreview: function ( context ) {
var lC = context.modules.intro;
// can't do this on less than three marks
if( lC.vizScene.objects.length < 3 ) return;
// move the camera way back
lC.vizScene.camera.position.x = -2000;
lC.vizScene.camera.position.z = -3000;
var targetMark = lC.vizScene.objects[lC.vizScene.objects.length - 2];
// tween the camera on down the line
var tween = new TWEEN.Tween( lC.vizScene.camera.position );
tween
.to( {
x: targetMark.position.x + (targetMark.bWidth / 2),
y: targetMark.position.y + (targetMark.bHeight / 2),
z: targetMark.position.z - 2000 }, 6000 )
.onComplete( function( ) {
delete lC.tweens['cameraEase'];
} )
.easing( TWEEN.Easing.Quartic.EaseInOut )
.start();
lC.tweens['cameraEase'] = tween;
$( lC.layerManager.layers['viz'].canvas )
.animate( { opacity: '1' }, 'slow' )
.delay( 2000 )
.animate( { opacity: '0.1'}, 'slow' );
// delay the dom animation
$( '#markmaker' )
.delay( 3000 );
},
startDomAnimation: function ( context ) {
var lC = context.modules.intro;
$( '#markmaker' )
.width( 0 )
.show()
.animate( { 'width': context.width }, 'slow', function () {
modules.intro.fn.startMarkAnimations( context );
$( '#intro-main-copy' ).fadeIn( 'slow' );
$( '#click-anywhere' ).delay( 200 ).fadeIn( 'slow' );
$( '#browse-marks' ).delay( 100 ).fadeIn( 'slow' );
} );
},
startMarkAnimations: function ( context ) {
var lC = context.modules.intro;
modules.intro.fn.drawX( context );
// start drawing the translated mark
if( lC.curLocaleMark ) {
var now = ( new Date() ).getTime() + 2000;
lC.textScene.timers[lC.curLocaleMark.reference] = { 'start': now, 'end': now + lC.curLocaleMark.maxTime, 'speed': 2 };
}
},
drawX: function ( context ) {
var lC = context.modules.intro;
// draw the X, with hardcoded data? good idea? maybe?
var xMarkData = {"strokes":[[{"x":177,"y":0,"z":0,"time":51,"speed":0,"angle":0,"significance":5},{"x":129,"y":60,"z":0,"time":255,"speed":0.45069390943299864,"angle":0.5880026035475675,"significance":1},{"x":123,"y":65,"z":0,"time":271,"speed":0.4931203163041915,"angle":0.7853981633974483,"significance":1},{"x":103,"y":89,"z":0,"time":339,"speed":0.45069390943299864,"angle":0.5880026035475675,"significance":1},{"x":56,"y":139,"z":0,"time":503,"speed":0.45073896963561083,"angle":0.7853981633974483,"significance":1},{"x":38,"y":162,"z":0,"time":584,"speed":0.3572203090978693,"angle":0.46364760900080615,"significance":1},{"x":9,"y":192,"z":0,"time":691,"speed":0.3535533905932738,"angle":0.7853981633974483,"significance":1},{"x":0,"y":206,"z":0,"time":727,"speed":0.45069390943299864,"angle":0.5880026035475675,"significance":5}],[{"x":11,"y":23,"z":0,"time":1178,"speed":0,"angle":0,"significance":5},{"x":30,"y":49,"z":0,"time":1246,"speed":0.32424352695503,"angle":5.639684198386302,"significance":1},{"x":55,"y":77,"z":0,"time":1321,"speed":0.48790367901871773,"angle":5.497787143782138,"significance":1},{"x":72,"y":100,"z":0,"time":1367,"speed":0.5216642390945547,"angle":5.695182703632019,"significance":1},{"x":85,"y":113,"z":0,"time":1408,"speed":0.5355917833779965,"angle":5.497787143782138,"significance":2},{"x":154,"y":175,"z":0,"time":1662,"speed":0.3311927108182759,"angle":5.497787143782138,"significance":1},{"x":186,"y":196,"z":0,"time":1802,"speed":0.2849548128987055,"angle":5.497787143782138,"significance":5}]],"country_code":"","time":1300925747439,"rtl":false,"maxTime":1802,"reference":"","hoverState":false,"renderedBounds":null,"id":null,"contributor_name":null,"extra_info":null,"color":"0,0,0","hoverColor":"0,139,211","x":454,"y":199,"position":{"x":0,"y":0,"z":0},"rotationAngle":{"x":0,"y":0,"z":0},"sX":0,"sY":0,"bWidth":186,"bHeight":206};
lC.xMark = new Mark.gmlMark( xMarkData.strokes, "xMark" );
lC.textScene.objects.push( lC.xMark );
var now = ( new Date() ).getTime();
lC.textScene.timers[lC.xMark.reference] = { 'start': now, 'end': now + lC.xMark.maxTime, 'speed': 1 };
},
// fallback function that doesn't bother with anything but the DOM elements
simpleIntro: function ( context ) {
var lC = context.modules.intro;
$( '#markmaker' )
.width( 0 )
.show()
.animate( { 'width': context.width }, 'slow', function () {
$( '#intro-main-copy' ).fadeIn( 'slow' );
$( '#click-anywhere' ).delay( 200 ).fadeIn( 'slow' );
$( '#browse-marks' ).delay( 100 ).fadeIn( 'slow' );
} );
}
}
};
}( jQuery ) );

317
ffdemo/static/assets/js/vendor/jquery.markApp.js поставляемый Normal file
Просмотреть файл

@ -0,0 +1,317 @@
( function( $ ) {
// The markApp namespace is used for storing available module code, instances of markApp, and any context free functions
$.markApp = {
// holds all available modules
modules: {},
// we keep track of all instances of markApp in here
instances: [],
// helper functions go here
fn: {}
};
// Creates a markApp instances with an element, and handles function calls on the instance
$.fn.markApp = function( options ) {
var $this = $( this );
// The context each markApp instance is stored as data on that element
var context = $this.data( 'markApp-context' );
// On first call, we need to set things up, but on all following calls we can skip right to the API handling
if ( !context || typeof context == 'undefined' ) {
context = {
// useful variables -- jquery objects get prefixed with $
app: null,
$container: $this,
frameCount: 0,
width: 0,
height: 0,
minWidth: 700,
minHeight: 500,
countries: [],
mouseX: null,
mouseY: null,
mouseDown: false,
mouseIn: false,
modules: {},
usersMark: null,
translatedStrings: {},
locale: ( window.location.pathname.split("/").length > 1 ) ? window.location.pathname.split("/")[1] : "en", // locale -- set by URL, default to 'en'
instance: $.markApp.instances.push( $this ) - 1, // store this instances index in the global instace array
// events
evt: {
resize: function( e ) {
var availableWidth = $( window ).width();
var availableHeight = $( window ).height() - ( $( 'header' ).height() + $( '#callout-boxes' ).height() );
if ( availableWidth < context.minWidth ) availableWidth = context.minWidth;
if ( availableHeight < context.minHeight ) availableHeight = context.minHeight;
context.$container.parent().width( availableWidth );
context.$container.parent().height( availableHeight );
context.width = availableWidth;
context.height = availableHeight;
// resize any elements with the autoResize class
$( '.autoResize' )
.height( availableHeight )
.width( availableWidth )
.trigger( 'resize.markApp', [availableWidth, availableHeight] );
},
mousemove: function( e ) {
context.mouseX = e.layerX;
context.mouseY = e.layerY;
},
mousedown: function( e ) {
if( 'preventDefault' in e ) e.preventDefault();
context.mouseDown = true;
},
mouseup: function( e ) {
if( 'preventDefault' in e ) e.preventDefault();
context.mouseDown = false;
},
mouseover: function( e ) {
if( 'preventDefault' in e ) e.preventDefault();
context.mouseIn = true;
},
mouseout: function( e ) {
if( 'preventDefault' in e ) e.preventDefault();
context.mouseX = null;
context.mouseY = null;
context.mouseIn = false;
},
ready: function ( e ) {
// refresh our translations
context.fn.loadTranslations();
}
},
// publicly accessible functions
public_fn: {
addModule: function( context, data ) {
var moduleName,
moduleOptions = {};
if( typeof data == "string" ) {
moduleName = data
} else if( typeof data == "object" ) {
for ( var moduleData in data ) {
moduleName = moduleData
moduleOptions = data[moduleData];
break;
}
}
if( $.markApp.modules[moduleName] ) {
// give this module it's own space for storing stuff if it doesn't already have it
context.modules[moduleName] = context.modules[moduleName] || {};
// if it has an init function, run it
if( 'init' in $.markApp.modules[moduleName].fn )
$.markApp.modules[moduleName].fn.init( context, moduleOptions );
}
},
unloadModule: function( context, moduleName ) {
if( moduleName == "all" ) {
// unload all the currently loaded modules
for ( moduleName in context.modules ) {
// if it has a deinit function, run it
if( 'deinit' in $.markApp.modules[moduleName].fn )
$.markApp.modules[moduleName].fn.deinit( context );
// remove it from our modules
delete context.modules[moduleName];
}
} else if ( moduleName in context.modules ) {
// if it has a deinit function, run it
if( 'deinit' in $.markApp.modules[moduleName].fn )
$.markApp.modules[moduleName].fn.deinit( context );
// remove it from our modules
delete context.modules[moduleName];
}
}
},
// internal functions
fn: {
// trigger event handlers on modules
trigger: function( eventName, eventObj, args ) {
// Add some assurances to our eventObj
if( typeof eventObj == "undefined" )
eventObj = { 'type': 'custom' };
// trigger the global handlers first
if ( eventName in context.evt ) {
// if it returns false, stop the train
if ( context.evt[eventName]( eventObj ) == false ) {
return false;
}
}
// run the event handler on each module that's got it
for( var module in context.modules ) {
if( module in $.markApp.modules &&
'evt' in $.markApp.modules[module] &&
eventName in $.markApp.modules[module].evt ) {
$.markApp.modules[module].evt[eventName]( context, eventObj, args );
}
}
},
loop: function( e ) {
// reset the delay
setTimeout( function() { context.fn.loop( ); }, 42 );
// incremenet the counter
context.frameCount++;
// dispatch the event
context.fn.trigger( 'loop', {}, [] );
},
// useful for delayed loading of the country data
withCountryCodes: function ( callback ) {
if ( 'US' in context.countries ) {
callback( context.countries );
} else {
$.ajax( {
'url': '/media/assets/js/vendor/country_codes.json',
'dataType': 'JSON',
'success': function ( data ) {
context.countries = data;
for( var i = 0; i < data.length; i++ ) {
context.countries[ data[i].code ] = data[i].name;
}
callback( context.countries );
},
'error': function () {
// handle error loading countries
}
} );
}
},
showLoader: function( msg, custom_class ) {
var custom_class = typeof custom_class === "string" ? custom_class : '';
var msg = typeof msg === "string" ? msg : context.fn.getString( 'default-loading-msg' );
// append our loader
var $loader = $( '<div />' )
.width( context.width )
.height( context.height )
.hide()
.addClass( 'overlay-wrapper autoResize' )
.addClass( custom_class )
.attr( 'id', 'markapp-loader' )
.append( $( '<div />' )
.text( msg ) );
context.$container
.append( $loader );
$loader.fadeIn( 'fast' );
},
hideLoader: function( ) {
$( '#markapp-loader' ).fadeOut( 'fast', function() {
$( this ).remove();
} );
},
showError: function ( msg ) {
var msg = typeof msg === "string" ? msg : context.fn.getString( 'default-error-msg' );
var $error = $( '<div />' )
.width( context.width )
.height( context.height )
.hide()
.click( function ( e ) {
e.preventDefault();
context.fn.hideError();
} )
.addClass( 'overlay-wrapper autoResize' )
.attr( 'id', 'markapp-error' )
.append( $( '<div />' )
.attr( 'id', 'markapp-error-content' )
.append( $( '<p />' ).html( msg ) ) );
context.$container
.append( $error );
$error.fadeIn( 'fast' );
},
hideError: function ( ) {
$( '#markapp-error' ).fadeOut( 'fast', function() {
$( this ).remove();
} );
},
// parses translated strings out of div.translated-strings
// ol's are treated as arrays
// everything else as is
loadTranslations: function( ) {
$( 'div.translated-strings' ).each( function () {
$( this ).children().each( function () {
var $this = $( this );
if ( $this.is( 'ol' ) ) {
// if it's an ol, load the strings in each childnode as an array
context.translatedStrings[$this.attr( 'id' )] = [];
$this.children().each( function () {
context.translatedStrings[$this.attr( 'id' )].push( $( this ).html() );
} );
} else {
// otherwise just load the elements contents
context.translatedStrings[$this.attr( 'id' )] = $this.html();
}
} );
} );
},
// looks for a match in translated stings
// returns the match if found, else it returns the key
getString: function ( key, index ) {
if( key in context.translatedStrings ) {
if( typeof context.translatedStrings[key] === "object" && typeof index === "number" ) {
// if this tranlsation is an array of strings, and we were passed an index val
return context.translatedStrings[key][index];
} else {
// otherwise just return the match
return context.translatedStrings[key];
}
} else {
// fallback to returning the passed key if we can't find the translation
return key;
}
},
storeData: function( key, value ) {
if ( typeof localStorage != 'undefined' ) {
// use localStorage if it exists
try {
if( typeof value === "object" ) {
value = JSON.stringify( value );
}
localStorage.setItem( key, value );
} catch (e) {
if ( e == QUOTA_EXCEEDED_ERR ) { /* data wasn't successfully saved due to quota exceed */ }
}
}
},
getData: function( key ) {
if ( typeof localStorage != 'undefined' ) {
var item = localStorage.getItem( key );
if( item ) {
if ( item[0]=="{" ) item = JSON.parse( item );
return item;
} else {
return false;
}
}
}
}
};
// bindings
$( window )
.delayedBind( 300, 'resize', function( e ) {
return context.fn.trigger( 'resize', e );
} )
.bind( 'keydown keypress keyup', function( e ) {
return context.fn.trigger( e.type, e );
} )
.trigger( 'resize' );
context.$container
.bind( 'mousemove mousedown mouseup mouseover mouseout ready swap', function( e ) {
return context.fn.trigger( e.type, e );
} );
// start the loop
context.fn.loop();
}
// Convert the arguments to an array to make this easier
var args = $.makeArray( arguments );
// handle public function calls
if ( args.length > 0 ) {
var call = args.shift();
if ( call in context.public_fn ) {
context.public_fn[call]( context, typeof args[0] == 'undefined' ? {} : args[0] );
}
}
return $this.data( 'markApp-context', context );
};
}( jQuery ) );

975
ffdemo/static/assets/js/vendor/jquery.markApp.linear.js поставляемый Normal file
Просмотреть файл

@ -0,0 +1,975 @@
( function( $ ) {
// support loose augmentation
markApp = $.markApp = $.markApp || {};
modules = $.markApp.modules = $.markApp.modules || {};
modules.linear = {
defaults: {
reference_mark: null, // optional reference mark -- will init the visualization on this mark if passed
country_code: null, // optional country code -- only loads marks with this county code if present
playback: false, // set this to true to immediately play back the initial mark
is_flagged: false, // optional flagged flag -- only loads marks that are flagged (for use within moderation)
linear_root: 'linear'
},
config: {
marks: {}, // all marks held here
flaggings: {}, // loaded from local storage, and used for some local checking that users are flagging things multiple times
orderedMarks: [], // array of mark references, arranged by id
leftBuffer: [], // mark buffers for the left and right of the marks currently in the scene
rightBuffer: [],
bufferSize: 20, // amount of marks to keep in a buffer
bufferMinSize: 5, // min amount of marks to keep in a buffer before forcing a refill
scene: null,
// handles camera changes -- could probably be shifted into the camera object
cameraChange: {},
tweens: {},
layerManager: null, // layer manager
initialized: false, // boolean flag to indicate if this module is fully setup
requestingMarks: false, // prevents more mark request from being sent when set to true
moreLeft: true, // Flags for the begining and end of the line. Assume we're not there at the start
moreRight: true,
hoverMark: null, // holds the mark currently being hovered over
currentMark: null,
playbackTimes: {}, // used for storing mark playback times by reference
eventChange: false // flag to tell the render loop if the mouse is causing changes that need rendered
},
evt: {
ready: function ( context, e ) {
modules.linear.fn.initInterface( context );
},
resize: function( context, e ) {
context.modules.linear.eventChange = true;
context.modules.linear.layerManager.resizeAll( context.width, context.height );
},
keydown: function ( context, e ) {
var lC = context.modules.linear;
switch( e.keyCode ) {
case 38:
// arrow up
e.preventDefault();
lC.cameraChange.aZ = 10;
break;
case 40:
// arrow down
e.preventDefault();
lC.cameraChange.aZ = -10;
break;
case 39:
// arrow right -- pan the camera to the right
e.preventDefault();
// next mark
lC.cameraChange.aX = 10;
// hide mark info
modules.linear.fn.hideMarkInformation( context );
break;
case 37:
// arrow left -- pan the camera to the left
e.preventDefault();
// prev mark
lC.cameraChange.aX = -10;
// hide mark info
modules.linear.fn.hideMarkInformation( context );
break;
}
},
keyup: function( context, e ) {
var lC = context.modules.linear;
switch( e.keyCode ) {
case 38:
case 40:
// arrow up
e.preventDefault();
lC.cameraChange.aZ = 0;
break;
case 39:
case 37:
e.preventDefault();
lC.cameraChange.aX = 0;
break;
}
},
// prevent our bound keys from firing native events
keypress: function( context, e ) {
switch( e.keyCode ) {
case 38:
case 40:
case 39:
case 37:
e.preventDefault();
break;
}
},
mousedown: function( context, e ) {
var lC = context.modules.linear;
if( mark = modules.linear.fn.hitTest( context, context.mouseX, context.mouseY ) ) {
// if the mark hasn't changed from what's in our URL, we just need to show the details again
if ( mark == lC.currentMark ) {
modules.linear.fn.centerCurrentMark( context, function() {
modules.linear.fn.showMarkInformation( context );
} );
// otherwise update the URL, and the module will jump to the referenced mark
} else if ( lC.country_code ) {
context.app.setLocation( '#/' + lC.linear_root + '/country/' + lC.country_code + '/' + mark.reference );
} else {
context.app.setLocation( '#/' + lC.linear_root + '/' + mark.reference );
}
}
},
mousemove: function( context, e ) {
var lC = context.modules.linear;
lC.eventChange = true;
// hover test
if( mark = modules.linear.fn.hoverTest( context, context.mouseX, context.mouseY, lC.hoverMark ) ) {
// reset the old hovered mark
if( lC.hoverMark ) lC.hoverMark.color = lC.hoverMark.contributor_name ? '0,139,211' : '0,0,0';
// store this hover mark
lC.hoverMark = mark;
if ( lC.currentMark && lC.hoverMark.reference == lC.currentMark.reference && lC.hoverMark.contributor_name && $( '#mark-information' ).is( ':visible' ) ) {
$( '#contributor-quote-box' )
.fadeIn( 'fast' )
.css( { left: context.mouseX - 15, top: context.mouseY - $( '#contributor-quote-box' ).height() - 15 } );
} else {
$( '#contributor-quote-box:visible' ).fadeOut( 'fast' );
lC.hoverMark.color = lC.hoverMark.contributor_name ? '255,111,40' : '0,139,211';
}
} else if ( lC.hoverMark ) {
lC.hoverMark.color = lC.hoverMark.contributor_name ? '0,139,211' : '0,0,0';
lC.hoverMark = null;
// fade out any contributor quotes we might happen to be showing
$( '#contributor-quote-box:visible' ).fadeOut( 'fast' );
}
},
loop: function ( context ) {
var lC = context.modules.linear,
dLayer = lC.layerManager.layers['drawingLayer'];
// update the position of the camera
var lastCameraPosition = {x: lC.scene.camera.position.x, y: lC.scene.camera.position.y, z: lC.scene.camera.position.z };
// two ways this can happen
if( 'cameraEase' in lC.tweens ) {
// with a tween
TWEEN.update();
} else {
// or with physics
lC.cameraChange.vZ += lC.cameraChange.aZ;
lC.cameraChange.vX += lC.cameraChange.aX;
lC.cameraChange.vX *= .93;
lC.cameraChange.vZ *= .93;
lC.scene.camera.position.x += lC.cameraChange.vX;
lC.scene.camera.position.z += lC.cameraChange.vZ;
// bring the Y and Z positions close to the current mark
var mark = modules.linear.fn.closestMark( context );
if ( mark ) {
var dY = lC.scene.camera.position.y - ( mark.position.y + ( mark.bHeight / 2 ) );
// if Z is not changing, try to maintain
// if( )
// var dZ = lC.scene.camera.position.z -
if ( dY != 0 && Math.abs(dY) >= 10) lC.scene.camera.position.y += ( dY > 0 ? -10 : 10 );
}
// TODO: fix the Z index while navigating the line
}
// if nothing has changed, return RIGHT NOW
if( !lC.eventChange &&
lastCameraPosition.x == lC.scene.camera.position.x &&
lastCameraPosition.y == lC.scene.camera.position.y &&
lastCameraPosition.z == lC.scene.camera.position.z ) {
return false;
}
// modified hover test -- only removes hover states
if( lC.hoverMark && ! modules.linear.fn.hoverTest( context, context.mouseX, context.mouseY, lC.hoverMark ) ) {
lC.hoverMark.color = lC.hoverMark.contributor_name ? '0,139,211' : '0,0,0';
lC.hoverMark = null;
// fade out any contributor quotes we might happen to be showing
$( '#contributor-quote-box:visible' ).fadeOut( 'fast' );
}
dLayer.clean();
// update scene marks && add more as needed
modules.linear.fn.updateScene( context, lC.scene.camera.position.x - 10000, lC.scene.camera.position.x + 10000 );
// cleanup the scene and tend to empty buffers
modules.linear.fn.updateBuffers( context, lC.scene.camera.position.x - 10000, lC.scene.camera.position.x + 10000 );
// render the scene
Mark.renderer.renderScene( lC.scene, { 'cursor': {x: context.mouseX, y: context.mouseY}, width: context.width, height: context.height } );
// ark.renderer.renderScene( lC.scene, dLayer.context, { x: context.mouseX, y: context.mouseY }, context.width, context.height, lC.playbackTimes );
// set the eventChange flag back to false -- a mouse/keyboard or playback event will need to set it back to true again before we'll render because of it
lC.eventChange = false
// cleanup playback times if necissary
for( mark in lC.scene.timers ) {
var now = ( new Date() ).getTime();
if( lC.scene.timers[mark].end < now ) {
delete lC.scene.timers[mark];
}
lC.eventChange = true;
}
}
},
fn: {
init: function( context, options ) {
var lC = context.modules.linear;
// if this modules has already been initialized, update the options
if ( '$linear' in context.modules.linear ) {
// before we merge options, check if we need to dump our current data
if ( options['country_code'] != lC.country_code ) {
modules.linear.fn.dumpAllMarks( context );
}
// now our options into our context
$.extend( lC, lC, options );
// since merging won't replace null or undefined values, make sure we clean up after it
for( option in modules.linear.defaults ) {
if ( options[option] == null ) lC[option] = modules.linear.defaults[option];
}
// update the interface
modules.linear.fn.updateInterface( context );
// load our marks
modules.linear.fn.initMarks( context );
} else {
// allow defaults to be overriden
$.extend( lC, modules.linear.defaults, options );
// but not the cofig
$.extend( lC, lC, modules.linear.config );
// DOM setup
lC.$linear = $( '<div />' )
.addClass( "linear-container" );
context.$container
.append( lC.$linear );
// scene setup
lC.scene = new Mark.scene();
lC.cameraChange = { aX: 0, aY: 0, aZ: 0, vX: 0, vY: 0, vZ: 0 };
// layer setup
lC.layerManager = new Mark.layerManager( lC.$linear.get( 0 ) );
lC.layerManager.addLayer( 'drawingLayer' );
lC.scene.canvasContext = lC.layerManager.layers['drawingLayer'].context;
// trigger resize so our new layers are sized to fit
context.fn.trigger( 'resize' );
lC.initialized = true;
}
// load flaggings
lC.flaggings = context.fn.getData( 'markFlaggings' ) || {};
},
deinit: function( context ) {
var lC = context.modules.linear;
lC.$linear.fadeOut( 'fast', function () {
// remove all our layers
lC.layerManager.removeAll();
lC.$linear.remove();
lC.initialized = false;
} );
},
initInterface: function( context ) {
var lC = context.modules.linear;
// hide anything that needs data loaded into it
$( '#stats' )
.hide( );
// enable the controls
$( '#mark-browsing-zoom-in a, #mark-browsing-zoom-out a, #mark-browsing-next a, #mark-browsing-prev a' )
.click( function( e ){ e.preventDefault(); } )
.bind( 'mouseup mouseout', function( e ) {
e.preventDefault();
if ( $( this ).data( 'mouseDown' ) ) {
if( $( this ).is( '#mark-browsing-zoom-in a, #mark-browsing-zoom-out a' ) ) {
context.modules.linear.cameraChange.aZ = 0;
} else if( $( this ).is( '#mark-browsing-next a, #mark-browsing-prev a' ) ) {
context.modules.linear.cameraChange.aX = 0;
}
$( this ).data( 'mouseDown', false );
}
} )
.bind( 'mousedown', function( e ) {
e.preventDefault();
$( this ).data( 'mouseDown', true );
if( $( this ).is( '#mark-browsing-zoom-in a, #mark-browsing-zoom-out a' ) ) {
context.modules.linear.cameraChange.aZ = $( this ).is( '#mark-browsing-zoom-in a' ) ? 10 : -10;
} else if ( $( this ).is( '#mark-browsing-next a, #mark-browsing-prev a' ) ) {
context.modules.linear.cameraChange.aX = $( this ).is( '#mark-browsing-next a' ) ? 10 : -10;
// hide the mark information
modules.linear.fn.hideMarkInformation( context );
}
} );
// populate our country filter select box
context.fn.withCountryCodes( function ( countryCodes ) {
var $select = $( '#country-select' );
for( var i = 0; i < countryCodes.length; i++ ) {
var $option = $( '<option />' )
.val( countryCodes[i].code )
.text( countryCodes[i].name );
$select.append( $option );
}
$select.change( function ( ) {
var val = $( this ).val();
if( val.length != 2 && lC.country_code ) {
// redirect back to the unfiltered view
context.app.setLocation( '#/' + lC.linear_root + '/' );
} else if ( val.length == 2 && val != lC.country_code ) {
// redirect to linear mode with the new country code
context.app.setLocation( '#/' + lC.linear_root + '/country/' + val + '/' );
}
// return focus to the viz
$( this ).blur();
context.$container.focus();
} );
// select the current mark if we have it
if( lC.country_code ) $select.val( lC.country_code );
} );
// hide all the mark detial things
$( '#mark-information' ).hide();
// setup the mark details
$( '#mark-playback' )
.click( function ( e ) {
e.preventDefault();
modules.linear.fn.replayCurrentMark( context );
} );
$( '#mark-flag' )
.click( function( e ) {
e.preventDefault();
modules.linear.fn.flagCurrentMark( context );
} );
// Click events for moderation mode
$( '#delete-mark' )
.click( function( e ) {
e.preventDefault();
modules.linear.fn.deleteCurrentMark( context );
} );
$( '#approve-mark-checkbox' )
.change( function( e ) {
e.preventDefault();
var shouldApprove = $(this).is(':checked');
modules.linear.fn.approveCurrentMark( context, shouldApprove );
} );
// setup sharing
$( '#twitter-share' ).socialShare( {
'share_url': 'http://twitter.com/share',
'share_params': { 'text': context.fn.getString( 'twitter-msg' ) }
} );
$( '#facebook-share' ).socialShare( {
'share_url': 'http://www.facebook.com/sharer.php',
'share_params': { 't': context.fn.getString( 'facebook-msg' ) }
} );
// run the interface update
modules.linear.fn.updateInterface( context );
// load our marks
modules.linear.fn.initMarks( context );
$("#sammy #country-select").selectBox({ autoWidth: false });
$("#sammy #contributor-select").selectBox({ autoWidth: false });
},
initMarks: function ( context ) {
var lC = context.modules.linear;
if ( lC.reference_mark && lC.reference_mark != "" ) {
// If we were passed a mark to start with, start there
if( lC.reference_mark in lC.marks ) {
// if we already have this mark, just jump to it
modules.linear.fn.jumpToMark( context, lC.reference_mark, lC.playback );
} else {
// load the mark and it's surrounding marks
modules.linear.fn.loadMarks( context, {
'reference': lC.reference_mark,
'include_forward': 20,
'include_back': 20,
'include_mark': 1,
'success': function ( data ) {
if( data.success ) {
// push the marks into the leftBuffer
modules.linear.fn.setupMarks( context, data.marks );
// and jump to our mark
modules.linear.fn.jumpToMark( context, lC.reference_mark, lC.playback );
} else {
// show the error message, with a link back to the main visualization link
context.fn.showError( lC.errorMsg );
}
lC.requestingMarks = false;
}
} );
}
} else {
// otherwise start at the first mark
modules.linear.fn.loadMarks( context, {
'offset': 0,
'max': 20,
'success': function ( data ) {
if( data.success ) {
modules.linear.fn.setupMarks( context, data.marks );
// after we load the marks, back the camera away and zoom to the first one
if( firstMark = lC.marks[data.marks[0].reference] ) {
lC.scene.camera.position.x = -4000;
lC.scene.camera.position.z = -3000;
var tween = 'cameraEase' in lC.tweens ? lC.tweens['cameraEase'] : new TWEEN.Tween( lC.scene.camera.position );
tween
.to( {
x: ( firstMark.bWidth / 2 ),
y: ( firstMark.bHeight / 2 ),
z: -1000 }, 2000 )
.onComplete( function( ) {
delete lC.tweens['cameraEase'];
if ( typeof callback === "function" ) callback( this );
} )
.easing( TWEEN.Easing.Quartic.EaseInOut )
.start();
lC.tweens['cameraEase'] = tween;
}
} else {
// show the error message, with a link back to the main visualization link
context.fn.showError( lC.errorMsg );
}
lC.requestingMarks = false;
}
} );
}
},
// DOM updates that should run after every new request should go here
updateInterface: function ( context ) {
var lC = context.modules.linear;
// show the appropriate middle link
var userMark = context.fn.getData( 'userMark' );
if( userMark && ( lC.country_code ? ( userMark.country_code == lC.country_code ) : true ) ) {
$( '#your-mark-link' )
.attr( 'href', '#/' + lC.linear_root + '/' + context.fn.getData( 'userMark' ).reference )
.show();
} else {
$( '#your-mark-link' )
.hide();
}
// setup the stats
var options = {};
if( lC.country_code ) {
options['country_code'] = lC.country_code;
$( "#contributor-select" ).next().hide();
$( '#contributor-select-label' ).hide();
} else {
$( "#contributor-select" ).next().show();
$( '#contributor-select-label' ).show();
}
// if the country has changed, grab updated data
if( ! $( '#mark-browsing-options' ).is( '.country-' + ( lC.country_code ? lC.country_code : 'all' ) ) ) {
$.ajax( {
'url': '/requests/init_viz_data',
'data': options,
dataType: 'JSON',
success: function( data ) {
// set the class on the details to indicate country
$( '#mark-browsing-options' )
.removeAttr( 'class' )
.addClass( 'country-' + ( lC.country_code ? lC.country_code : 'all' ) );
// setup and show the stats
$( '#stats-number-of-marks' )
.text( data.total_marks );
$( '#stats-number-of-countries' )
.text( data.total_countries );
var now = new Date();
var then = new Date( data.first_mark_at );
var days = Math.ceil( ( now.getTime() - then.getTime() ) / ( 1000 * 60 * 60 * 24 ) );
$( '#stats-number-of-days' )
.text( days );
$( '#stats' )
.fadeIn( 'fast' );
if( lC.country_code ) {
$( '#first-mark-link' )
.attr( 'href', '#/' + lC.linear_root + '/country/' + lC.country_code + '/' + data.country_first_mark );
$( '#last-mark-link' )
.attr( 'href', '#/' + lC.linear_root + '/country/' + lC.country_code + '/' + data.country_last_mark );
} else {
$( '#first-mark-link' )
.attr( 'href', '#/' + lC.linear_root + '/' + data.first_mark );
$( '#last-mark-link' )
.attr( 'href', '#/' + lC.linear_root + '/' + data.last_mark );
}
// setup collapsibles
$( '#mark-browsing' ).collapsibleMod( );
$( '#stats' ).collapsibleMod( { 'collapsedHeight': 10 } );
// if the contributor box is empty, fill it
if( $( '#contributor-select option' ).size() == 1 ) {
var $select = $( '#contributor-select' );
if ( data.contributor_marks ) {
for( var i = 0; i < data.contributor_marks.length; i++ ) {
var $option = $( '<option />' )
.val( data.contributor_marks[i].reference )
.text( data.contributor_marks[i].contributor );
$select.append( $option );
}
}
$select.change( function ( ) {
var val = $( this ).val();
if( val.length != "label" ) {
// jump to this contributors mark
context.app.setLocation( '#/' + lC.linear_root + '/' + val );
}
// return focus to the viz
$( this ).blur();
context.$container.focus();
} );
if( lC.country_code ) {
options['country_code'] = lC.country_code;
$( "#contributor-select" ).next().hide();
$( '#contributor-select-label' ).hide();
} else {
$( "#contributor-select" ).next().show();
$( '#contributor-select-label' ).show();
}
}
}
} );
}
},
dumpAllMarks: function ( context ) {
var lC = context.modules.linear;
for( mark in lC.marks )
delete lC.marks[mark];
lC.scene.objects = [];
lC.leftBuffer = [];
lC.rightBuffer = [];
lC.moreRight = true;
lC.moreLeft = true;
// whenever we do this, we need to set the camera back to 0,0,-1000
lC.scene.camera.position.x = 0;
lC.scene.camera.position.y = 0;
lC.scene.camera.position.z = -1000;
},
loadMarks: function( context, options ) {
var lC = context.modules.linear;
var callback = options.success;
delete options.success;
// pass the country code if we have one
if ( lC.country_code ) options.country_code = lC.country_code;
var url_to_load = options.reference ? '/requests/marks_by_reference' : '/requests/all_marks';
if (lC.is_flagged)
{
url_to_load = '/requests/marks_by_flagged';
}
// if we're loading based on a reference we dont have, we need to dump everything as we can't ensure we wont drop marks in between
if( options.reference && !( options.reference in lC.marks ) ) {
// if we're looking for a specific mark see if we already have that one
modules.linear.fn.dumpAllMarks( context );
context.fn.showLoader( context.fn.getString( 'loading-marks-msg' ), 'overlay-light' );
} else if ( ! options.reference ) {
context.fn.showLoader( context.fn.getString( 'loading-marks-msg' ), 'overlay-light' );
}
$.ajax( {
url: url_to_load,
data: options,
dataType: 'JSON'
} )
.success( callback )
.success( function ( data ) {
// hide the loader
context.fn.hideLoader();
} );
},
setupMarks: function( context, marks ) {
var lC = context.modules.linear;
// if this is empty, return
if( marks.length == 0 ) return;
// get rid of marks we've already got
for( var i = 0; i < marks.length; i++ ) {
if( marks[i].reference in lC.marks ) {
marks.shift( i, 1 );
}
}
// sort our current marks so we can tell what buffer to load these into
var sortedMarks = [];
for ( var mark in lC.marks )
sortedMarks.push( [mark, lC.marks[mark].id] );
sortedMarks.sort( function( a, b ) { return a[1] - b[1] } );
// default to the right buffer
var buffer = sortedMarks.length == 0 || sortedMarks[0][1] < marks[0].id ? lC.rightBuffer: lC.leftBuffer;
// var buffer = sortedMarks.length > 0 && sortedMarks[0][1] > marks[0].id ? lC.rightBuffer : lC.leftBuffer;
var reverse = buffer == lC.leftBuffer ? true : false;
if( reverse ) marks.reverse();
// try to establish a previous mark by which we can position the new marks
var pMark = buffer.length > 0 ?
lC.marks[buffer[ reverse ? 0 : buffer.length - 1 ]] :
lC.scene.objects[ reverse ? 0 : lC.scene.objects.length - 1];
for ( var i = 0; i < marks.length; i++ ) {
// if we already have this one, on to the next one
if( marks[i].reference in lC.marks ) continue;
var points_obj = JSON.parse( marks[i].points_obj_simplified );
// do some validation to make sure this mark wont break the viz
if( !( 'strokes' in points_obj ) ||
points_obj.strokes.length == 0 ||
points_obj.strokes[0].length < 2 ) continue;
var mark = new Mark.gmlMark( points_obj.strokes, marks[i].reference, marks[i].country_code, marks[i].date_drawn, points_obj.rtl, marks[i].id, marks[i].is_approved );
if( marks[i].contributor ) {
mark.contributor_name = marks[i].contributor;
mark.extra_info = points_obj.extra_info;
mark.color = '0,139,211';
}
// stash this mark
lC.marks[mark.reference] = mark;
// position this mark relative to the last one
if ( pMark ) mark.positionRelativeTo( pMark, reverse );
if( reverse ) {
buffer.unshift( mark.reference );
} else {
buffer.push( mark.reference );
}
pMark = mark;
}
// if ( !lC.currentMark ) lC.currentMark = mark;
},
// attempts to refill a buffer that's running low
refillBuffer: function( context, buffer ) {
var lC = context.modules.linear;
if( lC.requestingMarks ) return;
lC.requestingMarks = true;
var isLeft = buffer == lC.leftBuffer;
var lastMark = null;
lastMark = buffer.length > 0 ?
lC.marks[buffer[ isLeft ? 0 : buffer.length - 1 ]] :
lC.scene.objects[ isLeft ? 0 : lC.scene.objects.length - 1];
modules.linear.fn.loadMarks( context, {
'reference': lastMark.reference,
'include_forward': isLeft ? 0 : 20,
'include_back': isLeft ? 20 : 0,
'include_mark': 0,
'success': function ( data ) {
if( data.success ) {
// push the marks into the leftBuffer
modules.linear.fn.setupMarks( context, data.marks );
// if we got back less than we asked for, assume we're at the end
if ( data.marks.length == 0 ) {
lC[ isLeft ? 'moreLeft' : 'moreRight'] = false;
}
} else {
// if we got an error, assume we're at the end and don't allow more to be loaded
lC[ isLeft ? 'moreLeft' : 'moreRight'] = false;
}
lC.requestingMarks = false;
}
} );
},
// moves marks from the display to the buffers
// also will grab more marks if a buffer length sinks below a threshold
updateBuffers: function( context, xMin, xMax ) {
var lC = context.modules.linear;
if( lC.scene.objects.length == 0 ) return;
// look for marks that need moved into the left buffer
var mark = lC.scene.objects[0];
while( mark && mark.position.x + mark.bWidth < xMin ) {
lC.leftBuffer.push( lC.scene.objects.shift().reference );
mark = lC.scene.objects[0];
}
// look for marks that need moved into the right buffer
mark = lC.scene.objects[lC.scene.objects.length - 1];
while( mark && mark.position.x > xMax ) {
lC.rightBuffer.unshift( lC.scene.objects.pop().reference );
mark = lC.scene.objects[lC.scene.objects - 1];
}
// if either of our buffers are running low, load more marks
if ( lC.leftBuffer.length < 5 && lC.scene.objects.length > 0 && lC.moreLeft && !lC.requestingMarks ) {
modules.linear.fn.refillBuffer( context, lC.leftBuffer );
} else if ( lC.rightBuffer.length < 5 && lC.scene.objects.length > 0 && lC.moreRight && !lC.requestingMarks ) {
modules.linear.fn.refillBuffer( context, lC.rightBuffer );
}
},
// moves marks from the buffers to the display
updateScene: function( context, xMin, xMax) {
var lC = context.modules.linear;
if( lC.rightBuffer.length > 0 ) {
// look for marks that need added from the right buffer
var mark = lC.marks[lC.rightBuffer[0]];
while( mark && mark.position && mark.position.x < xMax ) {
lC.scene.objects.push( lC.marks[lC.rightBuffer.shift()] );
mark = lC.rightBuffer[0];
}
}
if( lC.leftBuffer.length > 0 ) {
// look for marks that need added from the left buffer
var mark = lC.marks[lC.leftBuffer[lC.leftBuffer.length - 1]];
while( mark && mark.position && mark.position.x + mark.bWidth > xMin ) {
lC.scene.objects.unshift( lC.marks[lC.leftBuffer.pop()] );
mark = lC.leftBuffer[lC.leftBuffer.length - 1];
}
}
},
jumpToMark: function( context, reference, playback ) {
var lC = context.modules.linear;
// find the mark
var oldMark = lC.currentMark;
lC.currentMark = lC.marks[reference];
// delay playback
if ( playback ) {
var wayLater = ( new Date() ).getTime() *2;
lC.scene.timers[reference] = { 'start': wayLater, 'end': wayLater + lC.currentMark.maxTime, 'speed': 1 };
}
modules.linear.fn.centerCurrentMark( context, function () {
// if we're playing it back, play it back
if( playback ) modules.linear.fn.replayCurrentMark( context );
// fade up those beautiful details if they're not already shown
modules.linear.fn.showMarkInformation( context );
} );
},
// unused but maybe helpful at some point?
// updatedOrderedMarks: function( context ) {
// var lC = context.modules.linear;
// var sortedMarks = [];
// for ( var mark in lC.marks )
// sortedMarks.push( [mark, lC.marks[mark].id] );
// sortedMarks.sort( function( a, b ) { return a[1] - b[1] } );
// // clear it out first
// lC.orderedMarks = [];
// for( var i = 0; i < sortedMarks.length; i++ ) {
// lC.orderedMarks.push( sortedMarks[i][0] );
// }
// },
// nextMark: function( context ) {
// var lC = context.modules.linear;
// var next = lC.orderedMarks[lC.orderedMarks.indexOf( lC.currentMark.reference ) + 1];
// if ( next ) {
// modules.linear.fn.jumpToMark( context, next );
// }
// },
// prevMark: function( context ) {
// var lC = context.modules.linear;
// var prev = lC.orderedMarks[lC.orderedMarks.indexOf( lC.currentMark.reference ) - 1];
// if ( prev ) {
// modules.linear.fn.jumpToMark( context, prev );
// }
// },
closestMark: function ( context ) {
var lC = context.modules.linear;
var retM = lC.scene.objects[0];
for( var i = 1; i < lC.scene.objects.length; i++ ) {
var nextM = lC.scene.objects[i];
var d = Math.abs( lC.scene.camera.position.x - ( nextM.position.x + ( nextM.bWidth / 2 ) ) );
var lastD = Math.abs( lC.scene.camera.position.x - ( retM.position.x + ( retM.bWidth / 2 ) ) );
if( d < lastD ) retM = nextM;
}
return retM;
},
// wrapper for the hit test method that offers preference to the optional curHoveredMark arg
hoverTest: function( context, x, y, curHoveredMark ) {
var lC = context.modules.linear;
// check if we're still over the hover mark
if( curHoveredMark &&
curHoveredMark.renderedBounds &&
x >= curHoveredMark.renderedBounds.minX &&
x <= curHoveredMark.renderedBounds.maxX &&
y >= curHoveredMark.renderedBounds.minY &&
y <= curHoveredMark.renderedBounds.maxY)
return curHoveredMark;
// if that didn't work we need to deffer to hit test
return modules.linear.fn.hitTest( context, x, y );
},
hitTest: function( context, x, y ) {
var lC = context.modules.linear;
// loop through displayed marks, see if any match these coords
for( var i = 0; i < lC.scene.objects.length; i++ ) {
if( lC.scene.objects[i].renderedBounds &&
x >= lC.scene.objects[i].renderedBounds.minX &&
x <= lC.scene.objects[i].renderedBounds.maxX &&
y >= lC.scene.objects[i].renderedBounds.minY &&
y <= lC.scene.objects[i].renderedBounds.maxY ) {
return lC.scene.objects[i];
}
}
return false;
},
showMarkInformation: function( context ) {
var lC = context.modules.linear;
if( !lC.currentMark ) return false;
var mark = lC.currentMark;
// update the information
$( '#mark-id' ).text( mark.id );
var d = new Date( mark.time );
// get our (hopefully) translated month abbreviation
var dateString = [];
dateString.push( context.fn.getString( 'month-abbreviations', d.getMonth() ) + " " + d.getDate() );
// dateString.push( $( '#month-abreviations li:eq(' + d.getMonth() + ')' ).text() + " " + d.getDate() );
dateString.push( d.getHours() + ":" + ( String(d.getMinutes()).length == 1 ? "0" + d.getMinutes() : d.getMinutes() ) );
if( mark.country_code ) {
context.fn.withCountryCodes( function ( countries ) {
$( '#mark-country' ).text( " / " + countries[mark.country_code] );
} );
} else {
$( '#mark-country' ).text( "" );
}
// set the contributor name, if we've got it
if( mark.contributor_name ) {
$( '#mark-contributor-name' ).text( "- " + mark.contributor_name );
$( '#mark-flag' ).hide();
$( '#contributor-quote' )
.text( mark.extra_info )
.html( "&#8220;" + $( '#contributor-quote' ).text() + "&#8221;" );
$( '#contributor-name' )
.text( "- " + mark.contributor_name );
} else {
$( '#mark-contributor-name, #contributor-quote, #contributor-name' ).text( "" );
$( '#mark-flag' ).show();
}
$( '#mark-timestamp' ).text( dateString.join( " / " ) );
$( '#url-share input' ).val( window.location.href );
// update the sharing links
if (lC.linear_root != "moderate")
{
$( '#twitter-share' )
.data( 'socialShare-context' ).share_params['url'] = window.location.href;
$( '#facebook-share' )
.data( 'socialShare-context' ).share_params['u'] = window.location.href;
}
// Update approved state if we're moderating // URK - hate this condition // Watch out // TODO
if (lC.linear_root == "moderate")
{
$("#approve-mark-checkbox").attr('checked', mark.is_approved);
}
// give the flag the appropriate class
if( lC.currentMark.reference in lC.flaggings ) {
$( '#mark-flag').addClass( 'disabled' );
} else {
$( '#mark-flag').removeClass( 'disabled' );
}
// animate it in if it's hidden
$( '#mark-information' ).fadeIn( 'fast' );
},
hideMarkInformation: function( context ) {
$( '#mark-information' ).fadeOut( 'fast' );
},
replayCurrentMark: function ( context ) {
var lC = context.modules.linear;
var now = ( new Date() ).getTime();
lC.eventChange = true;
lC.scene.timers[lC.currentMark.reference] = { 'start': now, 'end': now + lC.currentMark.maxTime, 'speed': 1 };
},
flagCurrentMark: function ( context ) {
var lC = context.modules.linear;
// if this user has already flagged this mark, return
if( lC.currentMark.reference in lC.flaggings ) return;
$.ajax( {
url: '/requests/flag_mark',
data: {
'reference': lC.currentMark.reference
},
type: 'POST',
dataType: 'JSON',
success: function( data ) {
// change the flag icon's class
$( '#mark-flag').addClass( 'disabled' );
// store the data that this user flagged this mark
lC.flaggings[lC.currentMark.reference] = true;
context.fn.storeData( 'markFlaggings', lC.flaggings );
},
error: function ( data ) {
context.fn.showError( lC.errorMsg );
}
} );
},
deleteCurrentMark: function ( context ) {
var lC = context.modules.linear;
$.ajax( {
url: '/requests/delete_mark',
data: {
'reference': lC.currentMark.reference
},
type: 'POST',
dataType: 'JSON',
success: function( data ) {
var deletedMarkReference = lC.currentMark.reference,
deletedMarkIndex = null;
// Delete current mark from marks data
delete lC.marks[lC.currentMark.reference];
lC.currentMark = null;
// remove the mark from the scene and reposition the rest
for ( var i=0; i < lC.scene.objects.length; i++ ) {
// start at the left and run through until you find the deleted mark
if( !deletedMarkIndex && lC.scene.objects[i].reference != deletedMarkReference ) continue;
// remove it
if( !deletedMarkIndex ) {
deletedMarkIndex = i;
lC.scene.objects.splice( i, 1 );
// set current mark to the next one
lC.currentMark = lC.scene.objects[i];
}
// reposition everything after it
if ( i == 0 ) {
lC.scene.objects[i].positionToStart( );
} else {
lC.scene.objects[i].positionRelativeTo( lC.scene.objects[i-1] );
}
}
// update the URL
context.app.setLocation( '#/' + lC.linear_root + '/' + lC.currentMark.reference );
lC.eventChange = true;
},
error: function ( data ) {
// TODO translate this msg
context.fn.showError( lC.errorMs );
}
} );
},
approveCurrentMark: function ( context, shouldApprove ) {
var lC = context.modules.linear;
$.ajax( {
url: '/requests/approve_mark',
data: {
'reference': lC.currentMark.reference,
'should_approve': shouldApprove
},
type: 'POST',
dataType: 'JSON',
success: function( data ) {
// Update approved status locally
lC.currentMark.is_approved = shouldApprove;
},
error: function ( data ) {
context.fn.showError( lC.errorMsg );
}
} );
},
centerCurrentMark: function( context, callback ) {
var lC = context.modules.linear;
if( !lC.currentMark ) return false;
modules.linear.fn.showMarkInformation( context );
// stop movement
lC.cameraChange.aX = 0;
lC.cameraChange.vX = 0;
lC.cameraChange.aZ = 0;
lC.cameraChange.vZ = 0;
// tween the camera to the mark
var speed = Math.abs( lC.currentMark.position.x - lC.scene.camera.position.x ) / 2;
speed = Math.max( 1000, speed );
// var speed = Math.min( 8000, Math.max( 1000, speed) );
var tween = 'cameraEase' in lC.tweens ? lC.tweens['cameraEase'] : new TWEEN.Tween( lC.scene.camera.position );
tween
.to( {
x: lC.currentMark.position.x + ( lC.currentMark.bWidth / 2 ),
y: lC.currentMark.position.y + ( lC.currentMark.bHeight / 2 ),
z: lC.currentMark.position.z - 1000 }, speed )
.onComplete( function( ) {
delete lC.tweens['cameraEase'];
if ( typeof callback === "function" ) callback( this );
} )
.easing( speed > 1200 ? TWEEN.Easing.Quadratic.EaseInOut : TWEEN.Easing.Quartic.EaseInOut )
.start();
lC.tweens['cameraEase'] = tween;
}
}
};
}( jQuery ) );

79
ffdemo/static/assets/js/vendor/jquery.socialShare.js поставляемый Normal file
Просмотреть файл

@ -0,0 +1,79 @@
/*
* A generic plugin to assist with those pesky social media buttons.
*
* Actually, you know what? This is more basic than that. It just opens up a new window with the dimensions you specify
* and the URL you specify, with the GET params you specify.
*
* That just happens to be the basic functionality you need to impliment a twitter or facebook share button.
*
* EXAMPLE:
* $( '#twitter-share' ).socialShare( {
* 'share_url': 'http://twitter.com/share',
* 'share_params': { 'text': 'Wow. I love this website. Check. It. Out', 'url': 'http://thegreatestwebsiteever.com/' }
* } );
*
* USEFUL FACEBOOK INFO:
* - url: http://www.facebook.com/sharer.php
* - required params:
* - u: the url you want to share
* - t: the title of the link you want to share
*
* USEFUL TWITTER INFO:
* - url: http://twitter.com/share
* - params:
* - url: the url you want to share
* - text: the message you want to tweet with this url
* - via: your twitter account, w/o the @, if you want attributed
* - related: a related account. Can also format it with a title like 'adammiller: People who dislike social media buttons'
*
*/
( function( $ ) {
$.socialShare = {
cfg: {
'$link': null,
'share_url': 'http://twitter.com/share',
'share_title': 'Share on Twitter',
'share_params': {},
'popupWidth': 550,
'popupHeight': 450
},
fn: {
'init': function ( container, options ) {
var $this = $( container );
var context = $.extend( {}, $.socialShare.cfg, options );
context.$link = $this;
context.$link.bind( 'click', function( e ) {
e.preventDefault();
$.socialShare.fn.share( context );
} );
$this.data( 'socialShare-context', context );
},
'shareURL': function ( context ){
var params = [];
for( param in context.share_params ) {
params.push( param + '=' + encodeURIComponent( context.share_params[param] ) );
}
return context.share_url + '?' + params.join( "&" );
},
'share': function ( context ) {
// open the pop up
window.open(
$.socialShare.fn.shareURL( context ),
context.share_title,
'height=' + context.popupHeight + ',width=' + context.popupWidth
);
}
}
};
$.fn.socialShare = function( options ) {
return $( this ).each( function () {
$.socialShare.fn.init( this, options );
} );
};
} )( jQuery );

822
ffdemo/static/assets/js/vendor/jquery.ui-selectBox.js поставляемый Normal file
Просмотреть файл

@ -0,0 +1,822 @@
/*
jQuery selectBox (version 0.1.6)
A cosmetic, styleable replacement for SELECT elements
Homepage: http://abeautifulsite.net/blog/2011/01/jquery-selectbox-plugin/
Demo page: http://labs.abeautifulsite.net/projects/js/jquery/selectBox/
Copyright 2011 A Beautiful Site, LLC.
License:
Licensed under both the MIT license and the GNU GPL (same as jQuery)
Usage:
Link to the JS file:
<script src="jquery.ui-selectbox.min.js" type="text/javascript"></script>
Add the CSS file (or append contents of your own):
<link href="jquery.ui-selectbox.css" rel="stylesheet" type="text/css" />
To create:
$("SELECT").selectBox()
To create with options:
$("SELECT").selectBox({
autoWidth: [true|false]
});
To destroy:
$("SELECT").selectBox('destroy');
To update the options on the fly:
$("SELECT").selectBox('setOptions', {
// Options are created like this
'value' : 'displayText',
'value' : 'displayText',
// Optgroups are created like this
'optgroupLabel' : {
'value' : 'displayText',
'value' : 'displayText'
}
});
To change the value:
$("SELECT").selectBox('value', 'optionValue');
Note: you can use any valid selector in lieu of "SELECT".
Events:
The focus, blur, and change events fire on the *orignal* SELECT element.
Freebies:
- Includes keyboard support (tab in/out, arrows, page up/down, home/end, enter/esc)
- Supports jQuery UI .ui-corner-x classes (http://jqueryui.com/docs/Theming/API#Corner_Radius_helpers)
- Uses CSS3 techniques (fully customizable via CSS)
Change log:
v0.1 (2011-01-24) - Initial release
v0.1.1 (2011-02-09) - Added setOptions method for changing options on the fly
- UI control now inherits all classes of the original control
v0.1.2 (2011-02-23) - UI control now inherits the style and title attribute of the original control
v0.1.3 (2011-02-24) - Added autoWidth option to simulate default browser behavior; fixed bug
that caused the UI control to display as inline instead of inline-block
after destroy/create; fixed version numbers (old 0.2 = 0.1.1, old 0.3 = 0.1.2)
v0.1.4 (2011-02-25) - Added 'value' method; added return $(this) to setOptions method
v0.1.5 (2011-03-11) - Fixed bug where special HTML characters did not get escaped properly in the UI control
v0.1.6 (2011-03-21) - Fixed bug where initial settings were forgotten when setOptions was called
Known issues:
- The change event fires every time an option is changed using the keyboard. This differs
from the way change events occur on normal select elements (on blur).
- Disabled controls will technically accept focus (but no event will be trigger) when tabbed
over. This differs from the default browser behavior where the control would normally be
skipped.
- If using the keyboard while the mouse is hovering over the dropdown, the hover events
sometimes conflict making it seem like the keyboard selection is buggy (move the mouse
out and the behavior goes away)
- The plugin cannot poll for changes to the original control (i.e. disabling it dynamically).
Since the dropdown gets re-generated each time it is shown, this isn't an issue with
optgroups and options. Calling scripts should be aware of this.
- Safari doesn't currently allow focus via tabbing (Chrome does; possible WebKit bug?)
- Does not support multiple="multiple"
- Not tested in IE6
Wish list:
- Enforce that dropdowns always appear in the viewport
- Predictive selection (auto-selecting of elements while typing)
Issue: keypress doesn't fire on non-input elements (only in Firefox,
but this is against the standard), so we have to use the keydown event.
There isn't a reliable way to map extended (i.e. non-ASCII) characters
without using the keypress event.
Aside from that, it should be easy enough to set a timer that waits
about two seconds after each keystroke before clearing the filter.
Then we just select the first option that matches the filter. This
feature should be available with or without the dropdown showing.
*/
if(jQuery) (function($) {
$.extend($.fn, {
selectBox: function(o, data) {
var _show = function(event) {
var select = event.data.select;
var control = event.data.control;
// Don't show disabled controls
if( $(control).hasClass('ui-selectBox-disabled') ) return false;
// Hide if the control is selected when the dropdown is already open
if( $(control).hasClass('ui-selectBox-focus') && $("#ui-selectBox-dropdown").size() === 1 ) {
_hide(event, true);
return false;
}
// Remove focus and dropdown from any/all other selectBoxes
$('.ui-selectBox').not(control).trigger('blur');
_focus(event);
event.stopPropagation();
// Generate the dropdown
$("#ui-selectBox-dropdown").remove();
var dropdown = $('<div id="ui-selectBox-dropdown" class="ui-corner-bottom" />');
var options = $('<ul />');
if( $(select).children('optgroup').size() === 0 ) {
$(select).children('option').each( function() {
var text = $(this).text() !== '' ? $(this).text() : '\u00A0';
var extraClasses = '';
if( $(this).attr('disabled') ) extraClasses += ' ui-selectBox-disabled';
$(options).append('<li class="ui-selectBox-option' + extraClasses + '">' + _htmlspecialchars(text) + '</li>');
});
} else {
$(dropdown).addClass('ui-selectBox-hasOptgroups');
$(select).children('optgroup').each( function() {
$(options).append('<li class="ui-selectBox-optgroup">' + _htmlspecialchars($(this).attr('label')) + '</li>');
$(this).children('option').each( function() {
var text = $(this).text() !== '' ? $(this).text() : '\u00A0';
var extraClasses = '';
if( $(this).attr('disabled') ) extraClasses += ' ui-selectBox-disabled';
$(options).append('<li class="ui-selectBox-option' + extraClasses + '">' + _htmlspecialchars(text) + '</li>');
});
});
}
// Add the options
$(dropdown).append(options);
// Select the appropriate option
var selectedIndex = $(select)[0].selectedIndex;
$(dropdown).find('LI.ui-selectBox-option').eq(selectedIndex).addClass('ui-selectBox-initial ui-selectBox-current');
// Add option events
$(dropdown).find('LI.ui-selectBox-option').hover( function() {
$(dropdown).find('.ui-selectBox-current').removeClass('ui-selectBox-current');
$(this).addClass('ui-selectBox-current');
}, function() {
$(this).removeClass('ui-selectBox-current');
}).click( { select: select, control: control }, function(event) {
_select(event);
}).mouseup( { select: select, control: control }, function(event) {
$(event.target).trigger('click');
});
// Position and display
$('BODY').append(dropdown);
var cPos = $(control).offset();
var cHeight = $(control).outerHeight();
var cWidth = $(control).outerWidth();
var borderAdjustment = parseInt($(dropdown).css('borderLeftWidth')) + parseInt($(dropdown).css('borderRightWidth'));
$(dropdown).css({
position: 'absolute',
zIndex: '999999',
top: cPos.top + cHeight,
left: cPos.left,
width: cWidth - borderAdjustment
}).show();
$(control).removeClass('ui-corner-all').addClass('ui-corner-top');
_disableSelection(dropdown);
_dropdownScrollFix(true);
};
var _hide = function(event, preventBlur) {
var select = event.data.select;
var control = event.data.control;
$("#ui-selectBox-dropdown").remove();
$(control).removeClass('ui-corner-top').addClass('ui-corner-all');
if( !preventBlur ) {
_blur(event);
} else {
$(control).focus();
}
};
var _select = function(event, option) {
var select = event.data.select;
var control = event.data.control;
option = option ? option : event.target;
if( $(option).hasClass('ui-selectBox-disabled') ) return false;
var oldSelectedIndex = $(select)[0].selectedIndex;
$('#ui-selectBox-dropdown .ui-selectBox-optgroup').remove();
var newSelectedIndex = $('#ui-selectBox-dropdown').find('LI.ui-selectBox-current').index();
if( oldSelectedIndex !== newSelectedIndex ) {
$(select)[0].selectedIndex = newSelectedIndex;
$(control).find('.ui-selectBox-label').text( $(option).text() );
$(select).trigger('change');
}
_hide(event, true);
};
var _focus = function(event) {
var select = event.data.select;
var control = event.data.control;
if( $(control).hasClass('ui-selectBox-disabled') ) return true;
if( $(control).hasClass('ui-selectBox-focus') ) return false;
// Remove dropdown and other focuses
$(".ui-selectBox.ui-selectBox-focus").removeClass("ui-selectBox-focus");
$("#ui-selectBox-dropdown").remove();
$(control).addClass('ui-selectBox-focus');
$(document).bind('mousedown', { select: select, control: control }, _blur);
$(document).bind('keydown', { select: select, control: control }, _key);
$(select).trigger('focus');
$(control).focus();
};
var _blur = function(event) {
var select = event.data.select;
var control = event.data.control;
// Prevent blur if the click was on the dropdown
if( event.target.id === 'ui-selectBox-dropdown' ||
$(event.target).parents('#ui-selectBox-dropdown').size() === 1 ) {
$(control).trigger('focus');
return false;
}
if( $(control).hasClass('ui-selectBox-focus') ) {
$(control).removeClass('ui-selectBox-focus');
$(document).unbind('mousedown', _blur);
$(document).unbind('keydown', _key);
$(select).trigger('blur');
_hide(event);
}
};
var _key = function(event) {
var select = event.data.select;
var control = event.data.control;
var dropdown = $("#ui-selectBox-dropdown");
if( $(control).hasClass('ui-selectBox-disabled') ) return false;
switch( event.keyCode ) {
case 9: // tab
_blur(event);
break;
case 13: // enter
if( $(dropdown).size() === 0 ) return false;
var siblings = $(dropdown).find('.ui-selectBox-option');
var currentIndex = -1;
$.each(siblings, function(index, option) {
if( $(option).hasClass('ui-selectBox-current') ) {
currentIndex = index;
return;
}
});
if( currentIndex >= 0 ) {
_select(event, $(siblings).eq(currentIndex));
}
return false;
break;
case 27: // esc
_hide(event, true);
break;
case 38: // up
case 37: // left
case 33: // page up
var interval = event.keyCode === 33 ? 20 : 1;
if( $(dropdown).size() === 0 ) {
if( event.altKey ) {
_show(event);
return false;
}
// Previous selection
var totalIndexes = $(select).find('OPTION').size(),
oldSelectedIndex = $(select)[0].selectedIndex,
newSelectedIndex = $(select)[0].selectedIndex - interval;
// Look for non-disabled option
while( $(select).find('OPTION').eq(newSelectedIndex).attr('disabled') === true && newSelectedIndex >= 0 ) {
newSelectedIndex--;
}
// Look for first enabled option
if( newSelectedIndex < 0 ) {
newSelectedIndex = $(select).find('OPTION:not([disabled]):first').index();
}
$(select)[0].selectedIndex = newSelectedIndex;
if( $(select)[0].selectedIndex === -1 ) {
newSelectedIndex = 0;
$(select)[0].selectedIndex = newSelectedIndex;
}
var label = $(select).find('OPTION:selected').text();
if( label === '' ) label = '\u00A0'; // &nbsp;
$(control).find('.ui-selectBox-label').text(label);
if( newSelectedIndex !== oldSelectedIndex ) $(select).trigger('change');
return false;
}
// Determine currently selected index (ignoring optgroup LIs)
var siblings = $(dropdown).find('.ui-selectBox-option');
var currentIndex = -1;
$.each(siblings, function(index, option) {
if( $(option).hasClass('ui-selectBox-current') ) {
currentIndex = index;
return;
}
});
currentIndex = currentIndex - interval;
if( currentIndex < 0 ) currentIndex = 0;
$(siblings).removeClass('ui-selectBox-current');
$(siblings).eq(currentIndex).addClass('ui-selectBox-current');
_dropdownScrollFix();
return false;
break;
case 40: // down
case 39: // right
case 34: // page down
var interval = event.keyCode === 34 ? 20 : 1;
if( $(dropdown).size() === 0 ) {
if( event.altKey ) {
_show(event);
return false;
}
var totalIndexes = $(select).find('OPTION').size(),
oldSelectedIndex = $(select)[0].selectedIndex,
newSelectedIndex = $(select)[0].selectedIndex + interval;
// Look for non-disabled option
while( $(select).find('OPTION').eq(newSelectedIndex).attr('disabled') === true && newSelectedIndex <= $(select).find('OPTION').size() ) {
newSelectedIndex++;
}
// Look for last enabled option
if( newSelectedIndex > totalIndexes - 1 ) {
newSelectedIndex = $(select).find('OPTION:not([disabled]):last').index();
}
$(select)[0].selectedIndex = newSelectedIndex;
if( $(select)[0].selectedIndex === -1 ) {
newSelectedIndex = $(select).find('OPTION').size() - 1;
$(select)[0].selectedIndex = newSelectedIndex;
}
var label = $(select).find('OPTION:selected').text();
if( label === '' ) label = '\u00A0'; // &nbsp;
$(control).find('.ui-selectBox-label').text(label);
if( newSelectedIndex != oldSelectedIndex ) $(select).trigger('change');
return false;
}
// Determine currently selected index (ignoring optgroup LIs)
var siblings = $(dropdown).find('.ui-selectBox-option');
var currentIndex = -1;
$.each(siblings, function(index, option) {
if( $(option).hasClass('ui-selectBox-current') ) {
currentIndex = index;
return;
}
});
currentIndex = currentIndex + interval;
if( currentIndex > $(siblings).size() - 1 ) currentIndex = $(siblings).size() - 1;
$(siblings).removeClass('ui-selectBox-current');
$(siblings).eq(currentIndex).addClass('ui-selectBox-current');
_dropdownScrollFix();
return false;
break;
case 36: // home
case 35: // end
if( $(dropdown).size() === 0 ) {
if( event.altKey ) {
_show(event);
return false;
}
var oldSelectedIndex = $(select)[0].selectedIndex,
newSelectedIndex;
if( event.keyCode === 36 ) {
// First
newSelectedIndex = 0;
} else {
// Last
newSelectedIndex = $(select).find('OPTION').size() - 1;
}
// Handle disabled options
if( $(select).find('OPTION').eq(newSelectedIndex).attr('disabled') === true ) {
if( event.keyCode === 36 ) {
newSelectedIndex = $(select).find('OPTION:not([disabled]):first').index();
} else {
newSelectedIndex = $(select).find('OPTION:not([disabled]):last').index();
}
}
$(select)[0].selectedIndex = newSelectedIndex;
var label = $(select).find('OPTION:selected').text();
if( label === '' ) label = '\u00A0'; // &nbsp;
$(control).find('.ui-selectBox-label').text(label);
if( newSelectedIndex != oldSelectedIndex ) $(select).trigger('change');
return false;
}
$(dropdown).find('.ui-selectBox-current').removeClass('ui-selectBox-current');
if( event.keyCode === 36 ) {
// First
$(dropdown).find('.ui-selectBox-option:first').addClass('ui-selectBox-current');
} else {
// Last
$(dropdown).find('.ui-selectBox-option:last').addClass('ui-selectBox-current');
}
_dropdownScrollFix();
return false;
break;
}
};
var _dropdownScrollFix = function(centerSelection) {
var dropdown = $("#ui-selectBox-dropdown");
if( $(dropdown).size() === 0 ) return false;
var target = $(dropdown).find('.ui-selectBox-current');
if( $(target).size() === 0 ) return false;
var targetTop = parseInt($(target).offset().top - $(dropdown).position().top);
var targetBottom = parseInt(targetTop + $(target).outerHeight());
if( centerSelection ) {
$(dropdown).scrollTop(
$(target).offset().top - $(dropdown).offset().top + $(dropdown).scrollTop() - ($(dropdown).height() / 2)
);
} else {
if( targetTop < 0 ) {
$(dropdown).scrollTop(
$(target).offset().top - $(dropdown).offset().top + $(dropdown).scrollTop()
);
}
if( targetBottom > $(dropdown).height() ) {
$(dropdown).scrollTop(
($(target).offset().top + $(target).outerHeight() ) - $(dropdown).offset().top + $(dropdown).scrollTop() - $(dropdown).height()
);
}
}
};
var _disableSelection = function(selector) {
$(selector)
.css('MozUserSelect', 'none')
.bind('selectstart', function() {
return false;
})
.bind('mousedown', function() {
return false;
});
return true;
};
var _htmlspecialchars = function(string) {
return( string.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;').replace(/'/g, '&#039;') );
};
switch( o ) {
case 'destroy':
$(this).each( function() {
var select = $(this);
var control = $(this).next('.ui-selectBox');
if( $(select)[0].tagName.toLowerCase() === 'select' ) {
$(control).remove();
$(select).removeData('selectBox-options').show();
}
});
return $(this);
break;
case 'disable':
$(this).each( function() {
var select = $(this);
var control = $(this).next('.ui-selectBox');
$(select).attr('disabled', true);
$(control).addClass('ui-selectBox-disabled');
});
return $(this);
break;
case 'enable':
$(this).each( function() {
var select = $(this);
var control = $(this).next('.ui-selectBox');
$(select).attr('disabled', false);
$(control).removeClass('ui-selectBox-disabled');
});
return $(this);
break;
case 'setOptions':
if( !data ) return $(this);
$(this).each( function() {
var select = $(this);
var control = $(this).next('.ui-selectBox');
switch( typeof(data) ) {
case 'string':
$(select).html(data);
break;
case 'object':
$(select).html('');
for( var i in data ) {
if( data[i] === null ) continue;
if( typeof(data[i]) === 'object' ) {
// OPTGROUP
var optgroup = $('<optgroup label="' + i + '" />');
for( var j in data[i] ) {
$(optgroup).append('<option value="' + j + '">' + data[i][j] + '</option>');
}
$(select).append(optgroup);
} else {
// OPTION
var option = $('<option value="' + i + '">' + data[i] + '</option>');
$(select).append(option);
}
}
break;
}
// Refresh the options
var options = $(select).data('selectBox-options');
$(select).selectBox('destroy');
$(select).selectBox(options);
});
return $(this);
break;
case 'value':
// Remove dropdown
$("#ui-selectBox-dropdown").remove();
$(this).each( function() {
var select = $(this);
var control = $(this).next('.ui-selectBox');
// Update value
$(select).val(data);
// Fix corners and update label
var label = $(select).find(':selected').text();
if( label === '' ) label = '\u00A0'; // &nbsp;
$(control).removeClass('ui-corner-top').addClass('ui-corner-all').find('.ui-selectBox-label').text(label);
});
return $(this);
break;
default:
// Create the control
$(this).each( function() {
// Default options
if( !o ) o = {};
var options = $.extend({
autoWidth: true
}, o);
var select = $(this);
if( $(this).next('.ui-selectBox').size() === 0 ) {
// Generate new control
var control = $('<a href="#" class="ui-selectBox ui-corner-all" tabindex="' + parseInt($(select).attr('tabindex')) + '" />');
// Inherit class names, style, and title attributes
$(control).addClass($(select).attr('class')).attr({
style: ($(select).attr('style') + '').replace(/inline/, 'inline-block'),
title: $(select).attr('title')
});
// Store options for later use
$(select).data('selectBox-options', options);
// Auto-width based on longest option
if( options.autoWidth ) {
// Determine character count of longest option
var longestOption = '';
$(select).find('OPTION').each( function() {
if( $(this).text().length > longestOption.length ) longestOption = $(this).text();
});
// Create a fake option, measure it, set the width, and remove the fake option
var div = $('<div class="ui-selectBox-dropdown" style="position: absolute; top: -9999em; left: -9999em; width: auto; display: inline-block;" />');
var li = $('<li class="ui-selectBox-option">' + _htmlspecialchars(longestOption) + '</li>');
$(div).append(li);
$('BODY').append(div);
$(control).width(li.outerWidth());
$(div).remove();
}
if( $(select)[0].tagName.toLowerCase() !== 'select' || $(select).attr('multiple') === true ) return;
if( $(select).attr('disabled') === true ) $(control).addClass('ui-selectBox-disabled');
var label = $(select).find('OPTION:selected').text();
if( label === '' ) label = '\u00A0'; // &nbsp;
// Add label and arrow
$(control).append('<span class="ui-selectBox-label">' + _htmlspecialchars(label) + '</span>');
$(control).append('<span class="ui-selectBox-arrow"></span>');
$(select).hide().after(control);
_disableSelection(control);
$(control)
.bind('click', function() { return false; })
.bind('mousedown', { select: select, control: control }, _show)
.bind('focus', { select: select, control: control }, _focus)
.bind('blur', { select: select, control: control }, _blur);
}
});
return $(this);
break;
}
}
});
})(jQuery);

478
ffdemo/static/assets/js/vendor/json2.js поставляемый Normal file
Просмотреть файл

@ -0,0 +1,478 @@
/*
http://www.JSON.org/json2.js
2008-11-19
Public Domain.
NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
See http://www.JSON.org/js.html
This file creates a global JSON object containing two methods: stringify
and parse.
JSON.stringify(value, replacer, space)
value any JavaScript value, usually an object or array.
replacer an optional parameter that determines how object
values are stringified for objects. It can be a
function or an array of strings.
space an optional parameter that specifies the indentation
of nested structures. If it is omitted, the text will
be packed without extra whitespace. If it is a number,
it will specify the number of spaces to indent at each
level. If it is a string (such as '\t' or '&nbsp;'),
it contains the characters used to indent at each level.
This method produces a JSON text from a JavaScript value.
When an object value is found, if the object contains a toJSON
method, its toJSON method will be called and the result will be
stringified. A toJSON method does not serialize: it returns the
value represented by the name/value pair that should be serialized,
or undefined if nothing should be serialized. The toJSON method
will be passed the key associated with the value, and this will be
bound to the object holding the key.
For example, this would serialize Dates as ISO strings.
Date.prototype.toJSON = function (key) {
function f(n) {
// Format integers to have at least two digits.
return n < 10 ? '0' + n : n;
}
return this.getUTCFullYear() + '-' +
f(this.getUTCMonth() + 1) + '-' +
f(this.getUTCDate()) + 'T' +
f(this.getUTCHours()) + ':' +
f(this.getUTCMinutes()) + ':' +
f(this.getUTCSeconds()) + 'Z';
};
You can provide an optional replacer method. It will be passed the
key and value of each member, with this bound to the containing
object. The value that is returned from your method will be
serialized. If your method returns undefined, then the member will
be excluded from the serialization.
If the replacer parameter is an array of strings, then it will be
used to select the members to be serialized. It filters the results
such that only members with keys listed in the replacer array are
stringified.
Values that do not have JSON representations, such as undefined or
functions, will not be serialized. Such values in objects will be
dropped; in arrays they will be replaced with null. You can use
a replacer function to replace those with JSON values.
JSON.stringify(undefined) returns undefined.
The optional space parameter produces a stringification of the
value that is filled with line breaks and indentation to make it
easier to read.
If the space parameter is a non-empty string, then that string will
be used for indentation. If the space parameter is a number, then
the indentation will be that many spaces.
Example:
text = JSON.stringify(['e', {pluribus: 'unum'}]);
// text is '["e",{"pluribus":"unum"}]'
text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t');
// text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]'
text = JSON.stringify([new Date()], function (key, value) {
return this[key] instanceof Date ?
'Date(' + this[key] + ')' : value;
});
// text is '["Date(---current time---)"]'
JSON.parse(text, reviver)
This method parses a JSON text to produce an object or array.
It can throw a SyntaxError exception.
The optional reviver parameter is a function that can filter and
transform the results. It receives each of the keys and values,
and its return value is used instead of the original value.
If it returns what it received, then the structure is not modified.
If it returns undefined then the member is deleted.
Example:
// Parse the text. Values that look like ISO date strings will
// be converted to Date objects.
myData = JSON.parse(text, function (key, value) {
var a;
if (typeof value === 'string') {
a =
/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value);
if (a) {
return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4],
+a[5], +a[6]));
}
}
return value;
});
myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) {
var d;
if (typeof value === 'string' &&
value.slice(0, 5) === 'Date(' &&
value.slice(-1) === ')') {
d = new Date(value.slice(5, -1));
if (d) {
return d;
}
}
return value;
});
This is a reference implementation. You are free to copy, modify, or
redistribute.
This code should be minified before deployment.
See http://javascript.crockford.com/jsmin.html
USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO
NOT CONTROL.
*/
/*jslint evil: true */
/*global JSON */
/*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", apply,
call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours,
getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join,
lastIndex, length, parse, prototype, push, replace, slice, stringify,
test, toJSON, toString, valueOf
*/
// Create a JSON object only if one does not already exist. We create the
// methods in a closure to avoid creating global variables.
if (!this.JSON) {
JSON = {};
}
(function () {
function f(n) {
// Format integers to have at least two digits.
return n < 10 ? '0' + n : n;
}
if (typeof Date.prototype.toJSON !== 'function') {
Date.prototype.toJSON = function (key) {
return this.getUTCFullYear() + '-' +
f(this.getUTCMonth() + 1) + '-' +
f(this.getUTCDate()) + 'T' +
f(this.getUTCHours()) + ':' +
f(this.getUTCMinutes()) + ':' +
f(this.getUTCSeconds()) + 'Z';
};
String.prototype.toJSON =
Number.prototype.toJSON =
Boolean.prototype.toJSON = function (key) {
return this.valueOf();
};
}
var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
gap,
indent,
meta = { // table of character substitutions
'\b': '\\b',
'\t': '\\t',
'\n': '\\n',
'\f': '\\f',
'\r': '\\r',
'"' : '\\"',
'\\': '\\\\'
},
rep;
function quote(string) {
// If the string contains no control characters, no quote characters, and no
// backslash characters, then we can safely slap some quotes around it.
// Otherwise we must also replace the offending characters with safe escape
// sequences.
escapable.lastIndex = 0;
return escapable.test(string) ?
'"' + string.replace(escapable, function (a) {
var c = meta[a];
return typeof c === 'string' ? c :
'\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
}) + '"' :
'"' + string + '"';
}
function str(key, holder) {
// Produce a string from holder[key].
var i, // The loop counter.
k, // The member key.
v, // The member value.
length,
mind = gap,
partial,
value = holder[key];
// If the value has a toJSON method, call it to obtain a replacement value.
if (value && typeof value === 'object' &&
typeof value.toJSON === 'function') {
value = value.toJSON(key);
}
// If we were called with a replacer function, then call the replacer to
// obtain a replacement value.
if (typeof rep === 'function') {
value = rep.call(holder, key, value);
}
// What happens next depends on the value's type.
switch (typeof value) {
case 'string':
return quote(value);
case 'number':
// JSON numbers must be finite. Encode non-finite numbers as null.
return isFinite(value) ? String(value) : 'null';
case 'boolean':
case 'null':
// If the value is a boolean or null, convert it to a string. Note:
// typeof null does not produce 'null'. The case is included here in
// the remote chance that this gets fixed someday.
return String(value);
// If the type is 'object', we might be dealing with an object or an array or
// null.
case 'object':
// Due to a specification blunder in ECMAScript, typeof null is 'object',
// so watch out for that case.
if (!value) {
return 'null';
}
// Make an array to hold the partial results of stringifying this object value.
gap += indent;
partial = [];
// Is the value an array?
if (Object.prototype.toString.apply(value) === '[object Array]') {
// The value is an array. Stringify every element. Use null as a placeholder
// for non-JSON values.
length = value.length;
for (i = 0; i < length; i += 1) {
partial[i] = str(i, value) || 'null';
}
// Join all of the elements together, separated with commas, and wrap them in
// brackets.
v = partial.length === 0 ? '[]' :
gap ? '[\n' + gap +
partial.join(',\n' + gap) + '\n' +
mind + ']' :
'[' + partial.join(',') + ']';
gap = mind;
return v;
}
// If the replacer is an array, use it to select the members to be stringified.
if (rep && typeof rep === 'object') {
length = rep.length;
for (i = 0; i < length; i += 1) {
k = rep[i];
if (typeof k === 'string') {
v = str(k, value);
if (v) {
partial.push(quote(k) + (gap ? ': ' : ':') + v);
}
}
}
} else {
// Otherwise, iterate through all of the keys in the object.
for (k in value) {
if (Object.hasOwnProperty.call(value, k)) {
v = str(k, value);
if (v) {
partial.push(quote(k) + (gap ? ': ' : ':') + v);
}
}
}
}
// Join all of the member texts together, separated with commas,
// and wrap them in braces.
v = partial.length === 0 ? '{}' :
gap ? '{\n' + gap + partial.join(',\n' + gap) + '\n' +
mind + '}' : '{' + partial.join(',') + '}';
gap = mind;
return v;
}
}
// If the JSON object does not yet have a stringify method, give it one.
if (typeof JSON.stringify !== 'function') {
JSON.stringify = function (value, replacer, space) {
// The stringify method takes a value and an optional replacer, and an optional
// space parameter, and returns a JSON text. The replacer can be a function
// that can replace values, or an array of strings that will select the keys.
// A default replacer method can be provided. Use of the space parameter can
// produce text that is more easily readable.
var i;
gap = '';
indent = '';
// If the space parameter is a number, make an indent string containing that
// many spaces.
if (typeof space === 'number') {
for (i = 0; i < space; i += 1) {
indent += ' ';
}
// If the space parameter is a string, it will be used as the indent string.
} else if (typeof space === 'string') {
indent = space;
}
// If there is a replacer, it must be a function or an array.
// Otherwise, throw an error.
rep = replacer;
if (replacer && typeof replacer !== 'function' &&
(typeof replacer !== 'object' ||
typeof replacer.length !== 'number')) {
throw new Error('JSON.stringify');
}
// Make a fake root object containing our value under the key of ''.
// Return the result of stringifying the value.
return str('', {'': value});
};
}
// If the JSON object does not yet have a parse method, give it one.
if (typeof JSON.parse !== 'function') {
JSON.parse = function (text, reviver) {
// The parse method takes a text and an optional reviver function, and returns
// a JavaScript value if the text is a valid JSON text.
var j;
function walk(holder, key) {
// The walk method is used to recursively walk the resulting structure so
// that modifications can be made.
var k, v, value = holder[key];
if (value && typeof value === 'object') {
for (k in value) {
if (Object.hasOwnProperty.call(value, k)) {
v = walk(value, k);
if (v !== undefined) {
value[k] = v;
} else {
delete value[k];
}
}
}
}
return reviver.call(holder, key, value);
}
// Parsing happens in four stages. In the first stage, we replace certain
// Unicode characters with escape sequences. JavaScript handles many characters
// incorrectly, either silently deleting them, or treating them as line endings.
cx.lastIndex = 0;
if (cx.test(text)) {
text = text.replace(cx, function (a) {
return '\\u' +
('0000' + a.charCodeAt(0).toString(16)).slice(-4);
});
}
// In the second stage, we run the text against regular expressions that look
// for non-JSON patterns. We are especially concerned with '()' and 'new'
// because they can cause invocation, and '=' because it can cause mutation.
// But just to be safe, we want to reject all unexpected forms.
// We split the second stage into 4 regexp operations in order to work around
// crippling inefficiencies in IE's and Safari's regexp engines. First we
// replace the JSON backslash pairs with '@' (a non-JSON character). Second, we
// replace all simple value tokens with ']' characters. Third, we delete all
// open brackets that follow a colon or comma or that begin the text. Finally,
// we look to see that the remaining characters are only whitespace or ']' or
// ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval.
if (/^[\],:{}\s]*$/.
test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@').
replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']').
replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
// In the third stage we use the eval function to compile the text into a
// JavaScript structure. The '{' operator is subject to a syntactic ambiguity
// in JavaScript: it can begin a block or an object literal. We wrap the text
// in parens to eliminate the ambiguity.
j = eval('(' + text + ')');
// In the optional fourth stage, we recursively walk the new structure, passing
// each name/value pair to a reviver function for possible transformation.
return typeof reviver === 'function' ?
walk({'': j}, '') : j;
}
// If the text is not JSON parseable, then a SyntaxError is thrown.
throw new SyntaxError('JSON.parse');
};
}
})();

13
ffdemo/static/assets/js/vendor/mark/mark.base.js поставляемый Normal file
Просмотреть файл

@ -0,0 +1,13 @@
var Mark = ( function ( mark ) {
// Basic classes for use throughout Mark
mark.vector = function ( x, y, z ) {
this.x = x || 0;
this.y = y || 0;
this.z = z || 0;
}
return mark;
}( Mark || {} ) );

218
ffdemo/static/assets/js/vendor/mark/mark.brushes.js поставляемый Normal file
Просмотреть файл

@ -0,0 +1,218 @@
var Mark = ( function ( mark ) {
mark.dof = 10000;
mark.thickMarkBrush = function( g, strokes, translateOptions, colorBase, w, h ) {
var colorBase = colorBase ? colorBase : '0,0,0';
// setup our starting bound
var p = Mark.renderer.translatePoint( strokes[0][0], translateOptions );
var bounds = { minX: p.x, maxX: p.x, minY: p.y, maxY: p.y };
// if this mark isn't even close to being on screen, just return
if ( ( w && h ) && ( p.x > w*2 || p.x < -w || p.y > h*2 || p.y < -h || p.z > mark.dof || p.z < 0 ) ) return;
// iterate
for( var i = 0; i < strokes.length; i++ ) {
if( typeof( strokes[i] ) == "undefined" || strokes[i].length <= 1 ) continue;
var prevP = null;
for( var j=0; j < strokes[i].length; j++ ) {
var p = Mark.renderer.translatePoint( strokes[i][j], translateOptions );
// if this point is out of the depth of field, on to the next one
if( p.z && ( p.z > mark.dof ) ) continue;
// if this point isn't significant enough, on to the next one
if( p.significance && p.significance * ( mark.dof / 5 ) < p.z - 500 ) continue;
// if this is the first point in a stroke
if( !prevP ) {
// if this isn't the first stroke in this mark, and this mark is a reasonable distance from the camera, connect them
if( i != 0 && p.z < 1500 ) {
var lastStrokePoint = Mark.renderer.translatePoint( strokes[i-1][strokes[i-1].length - 1], translateOptions );
if( lastStrokePoint.z && lastStrokePoint.z < mark.dof ) {
g.strokeStyle = 'rgba(0,0,0,0.3)';
g.lineWidth = 1;
g.beginPath();
g.dashedLineTo( lastStrokePoint.x, lastStrokePoint.y, p.x, p.y, [6,4] );
g.closePath();
g.stroke();
}
}
prevP = p;
prevPX = 0;
prevPY = 0;
continue;
}
g.lineWidth = 1;
// set line width
if ( j == strokes[i].length - 1 ) {
// if this is the last point, make the widht 0 so we dont have a blunt end
var px = 0;
var py = 0;
} else {
var distance = 9 - Math.max( 0, Math.pow(p.speed + 1, 3) );
if( translateOptions.mode == "flatScale" && translateOptions.scale.thickness ) {
distance *= translateOptions.scale.thickness;
} else {
if( p.z ) distance *= (2/p.z) * ( h / 2 );
}
if ( distance < 0.1 ) distance = 0.1;
distance += 1;
var px = Math.cos( p.angle ) * distance;
var py = Math.sin( p.angle ) * distance;
}
g.strokeStyle = 'rgba(' + colorBase + ',' + ( ( mark.dof - p.z ) / mark.dof )+')';
g.fillStyle = 'rgba(' + colorBase + ',' + ( ( mark.dof - p.z ) / mark.dof )+')';
try {
g.beginPath();
g.lineWidth = 0.5 * ( ( mark.dof - p.z ) / mark.dof );
g.moveTo( prevP.x - prevPX - 0.5, prevP.y - prevPY - 0.5 );
g.lineTo( prevP.x + prevPX - 0.5, prevP.y + prevPY - 0.5 );
g.lineTo( p.x + px - 0.5, p.y + py - 0.5 );
g.lineTo( p.x - px - 0.5, p.y - py - 0.5 );
g.lineTo( prevP.x - prevPX - 0.5, prevP.y - prevPY - 0.5 );
g.fill();
g.stroke();
} catch( e ) {
// console.error( p, prevX, prevY, prevT, px, py, dx, dy, dt );
}
// expand bounds
bounds.minX = p.x < bounds.minX ? p.x : bounds.minX;
bounds.minY = p.y < bounds.minY ? p.y : bounds.minY;
bounds.maxX = p.x > bounds.maxX ? p.x : bounds.maxX;
bounds.maxY = p.y > bounds.maxY ? p.y : bounds.maxY;
// stash variables for later
prevP = p
prevPX = px;
prevPY = py;
}
}
// return our bounds for storage
return bounds;
};
mark.connectionBrush = function( g, p1, p2, offset1, offset2, w, h ) {
var tO = { offset: offset1, w: w, h: h, mode: 'pinhole' };
p1 = Mark.renderer.translatePoint( p1, tO );
tO.offset = offset2;
p2 = Mark.renderer.translatePoint( p2, tO );
// if these points are both off the screen, don't render the line
if ( ( p1.x > w || p1.x < 0 || p1.y > h || p1.y < 0 ) && ( p2.x > w || p2.x < 0 || p2.y > h || p2.y < 0 ) ) return;
// if these points are out of the dof, don't render the line
if ( p1.z && p2.z && ( p1.z > mark.dof || p1.z < 0 ) && ( p2.z > mark.dof || p2.z < 0 ) ) return;
g.strokeStyle = 'rgba(0,0,0,' + ( ( mark.dof - p1.z ) / (mark.dof*2) )+')';
var distance = 3 * (2/p1.z) * ( h / 2 );
if ( distance < 1 ) distance = 1;
g.lineWidth = distance;
g.beginPath();
g.moveTo( p1.x, p1.y );
g.lineTo( p2.x, p2.y );
g.closePath();
g.stroke();
},
mark.thickBrush = function( g, strokes, offsetX, offsetY, depth ) {
var offsetX = offsetX ? offsetX : 0;
var offsetY = offsetY ? offsetY : 0;
var depth = depth ? depth : 1;
for( var i = 0; i < strokes.length; i++ ) {
if( typeof( strokes[i] ) == "undefined" || strokes[i].length <= 1 ) continue;
// if this is the first point in a stroke
if( i > 0 ) {
// connect to the last stroke
g.strokeStyle = 'rgba(0,0,0,0.1)';
g.lineWidth = 1;
g.beginPath();
var lastX = strokes[i-1][strokes[i-1].length - 1].x,
lastY = strokes[i-1][strokes[i-1].length - 1].y,
firstX = strokes[i][0].x,
firstY = strokes[i][0].y;
g.dashedLineTo( lastX + offsetX, lastY + offsetY, firstX + offsetX, firstY + offsetY, [6,4] );
g.closePath();
g.stroke();
}
g.lineWidth = 1;
g.strokeStyle = '#000000';
g.fillStyle = '#000000';
var prevPX = 0,
prevPY = 0,
prevT = strokes[i][0].time,
prevX = strokes[i][0].x,
prevY = strokes[i][0].y ;
for( var j=1; j < strokes[i].length; j++ ) {
var p = strokes[i][j];
// if this point is insignificant, on to the next one
if( p.significance < depth ) continue;
// if this is the last stroke
if ( j == strokes[i].length - 1 ) {
var px = 0;
var py = 0;
} else {
var distance = 9 - Math.pow(p.speed + 1, 3);
if ( distance < 0.5 ) distance = 0.5;
distance += 1;
var px = Math.cos( p.angle ) * distance;
var py = Math.sin( p.angle ) * distance;
}
try {
g.beginPath();
g.moveTo( prevX - prevPX - 0.5 + offsetX, prevY - prevPY - 0.5 + offsetY );
g.lineTo( prevX + prevPX - 0.5 + offsetX, prevY + prevPY - 0.5 + offsetY );
g.lineTo( p.x + px - 0.5 + offsetX, p.y + py - 0.5 + offsetY );
g.lineTo( p.x - px - 0.5 + offsetX, p.y - py - 0.5 + offsetY );
g.lineTo( prevX - prevPX - 0.5 + offsetX, prevY - prevPY - 0.5 + offsetY );
g.fill();
g.stroke();
} catch( e ) {
// console.error( p, prevX, prevY, prevT, px, py, dx, dy, dt );
}
prevPX = px;
prevPY = py;
prevX = p.x;
prevY = p.y;
prevT = p.time;
prevAng = p.angle;
}
}
};
mark.circleMarkBrush = function( g, strokes, offsetParent ) {
var d = 3;
for( var i = 0; i < strokes.length; i++ ) {
if( strokes[i].length == 0 ) continue;
for( var j=0; j < strokes[i].length; j++ ) {
var p = Mark.renderer.translatePoint( strokes[i][j], offsetParent );
if( p.z && p.z > mark.dof ) continue;
d = 3 * ( ( mark.dof - p.z ) / mark.dof );
g.fillStyle = "rgba(255,255,255,0.4)";
g.strokeStyle = "rgba(255,255,255,0.4)";
g.beginPath();
g.arc(p.x, p.y, d, 0, Math.PI*2, true);
g.closePath();
g.fill();
g.stroke();
g.lineTo( p.x, p.y );
}
}
};
mark.circleBrush = function( g, strokes, offsetX, offsetY, depth ) {
var offsetX = offsetX ? offsetX : 0;
var offsetY = offsetY ? offsetY : 0;
var depth = depth ? depth : 1;
for( var i = 0; i < strokes.length; i++ ) {
if( strokes[i].length == 0 ) continue;
var p1 = strokes[i][0];
g.beginPath();
for( var j=0; j < strokes[i].length; j++ ) {
var p = strokes[i][j];
// if this point is insignificant, on to the next one
if( p.significance < depth ) continue;
g.beginPath();
g.arc(p.x + offsetX, p.y + offsetY, 3, 0, Math.PI*2, true);
g.closePath();
g.fill();
g.stroke();
g.lineTo( p.x + offsetX, p.y + offsetY );
}
}
};
return mark;
}( Mark || {} ) );

14
ffdemo/static/assets/js/vendor/mark/mark.camera.js поставляемый Normal file
Просмотреть файл

@ -0,0 +1,14 @@
var Mark = ( function ( mark ) {
// This is currently very rudimentry
// no projection math happening, just some offsets to consult when rendering marks
mark.camera = function ( ) {
this.position = new mark.vector( 0, 0 ,-1000 );
};
return mark;
}( Mark || {} ) );

166
ffdemo/static/assets/js/vendor/mark/mark.gmlMark.js поставляемый Normal file
Просмотреть файл

@ -0,0 +1,166 @@
var Mark = ( function ( mark ) {
mark.gmlMark = function( strokes, reference, country_code, time, rtl, id, is_approved ) {
this.strokes = strokes;
this.country_code = country_code;
this.time = time;
this.rtl = rtl;
this.maxTime = 0;
this.reference = reference;
this.hoverState = false;
this.renderedBounds = null;
this.id = id ? id : null;
this.is_approved = is_approved;
this.contributor_name = null;
this.extra_info = null;
// colors for this mark
this.color = '0,0,0';
// current position of this mark
this.x = 0;
this.y = 0;
// we represent the mark as a plane sitting in 3d space -- all point positions can then be calculatd relative to this
// top left corner of the bounding box
this.position = {x: 0, y: 0, z: 0};
// angle of the plane in 3d space -- values are between -1 and 1, and really we only want to allow rotation around the Y axis
this.rotationAngle = {x: 0, y: 0, z: 0};
// offset of the start point from the origin of the bounding box
this.sX = 0;
this.sY = 0;
// bounding box dimensions
this.bWidth = 0;
this.bHeight = 0;
this.init = function () {
if( this.strokes.length > 0 ) {
this.setupVars();
}
};
this.setupVars = function () {
this.maxTime = this.lastPoint().time;
this.getBoundingBox();
};
// looks for and returns the start of a stroke that is the furthes to the left
this.leftmostStrokeStart = function () {
var firstPoint = this.strokes[0][0];
for ( var i = 1; i < this.strokes.length; i++ ) {
var p = this.strokes[i][0];
if ( p.x < firstPoint.x ) lastPoint = p;
}
return firstPoint;
}
// looks for and returns the end of a stroke that is furthest to the right
this.rightmostStrokeEnd = function () {
// start with the last point of the first stroke
var lastPoint = this.strokes[0][this.strokes[0].length - 1];
for ( var i = 1; i < this.strokes.length; i++ ) {
var p = this.strokes[i][this.strokes[i].length - 1];
if ( p.x > lastPoint.x ) lastPoint = p;
}
return lastPoint;
};
// returns the very first point drawn in this stroke
this.firstPoint = function () {
return this.strokes[0][0];
};
// returns the very last point drawn in this stroke
this.lastPoint = function () {
return this.strokes[this.strokes.length - 1][this.strokes[this.strokes.length - 1].length - 1];
};
this.translatePoint = function ( point ) {
var tP = point.clone();
tP.x = this.x + point.x;
tP.y = this.y + point.y;
return tP;
};
this.getBoundingBox = function () {
var p1 = this.strokes[0][0];
var maxX = p1.x, minX = p1.x, maxY = p1.y, minY= p1.y;
for( var i = 0; i < this.strokes.length; i ++ ) {
for( var j = 0; j < this.strokes[i].length; j++ ) {
var p = this.strokes[i][j];
maxX = p.x > maxX ? p.x : maxX;
maxY = p.y > maxY ? p.y : maxY;
minX = p.x < minX ? p.x : minX;
minY = p.y < minY ? p.y : minY;
}
}
this.bWidth = maxX - minX;
this.bHeight = maxY - minY;
this.x = minX;
this.y = minY;
if ( minX != 0 || minY != 0 )
this.fitPointsToBounds( minX, minY );
};
this.fitPointsToBounds = function ( minX, minY ) {
for( var i = 0; i < this.strokes.length; i ++ ) {
for( var j = 0; j < this.strokes[i].length; j++ ) {
this.strokes[i][j].x -= minX;
this.strokes[i][j].y -= minY;
}
}
};
// returns a subset of this marks strokes, at a given time
this.strokesAtTime = function ( time ) {
if( time > this.maxTime ) return this.strokes;
var sat = [[]];
var curIndex = [0, 0]
var nextPoint = this.strokes[curIndex[0]][curIndex[1]];
while( nextPoint.time < time ) {
sat[sat.length - 1].push( nextPoint );
curIndex[1]++;
if( this.strokes[curIndex[0]].length == curIndex[1] ) {
curIndex[0]++;
curIndex[1] = 0;
sat.push([]);
}
nextPoint = this.strokes[curIndex[0]][curIndex[1]];
}
return sat;
};
// If reverse is true, we position relative to the start of the mark
// if it's false, or ommitted, we position relative to the end of the mark
this.positionRelativeTo = function ( mark, reverse ) {
var reverse = reverse ? !!reverse : false;
var buffer = 50;
if ( reverse ) {
this.position.x = mark.position.x - buffer - this.bWidth;
// this.x = mark.x + mark.firstPoint().x - this.lastPoint().x;
this.position.y = mark.position.y + mark.leftmostStrokeStart().y - this.rightmostStrokeEnd().y;
// this is based on a static computation in mark.renderer
// if you change it there, change it here
this.position.z = mark.position.z - ( this.maxTime / 50 );
} else {
this.position.x = mark.position.x + mark.bWidth + this.leftmostStrokeStart().x + buffer;
// this.x = mark.x + mark.rightmostStrokeEnd().x - this.leftmostStrokeStart().x;
this.position.y = mark.position.y + mark.rightmostStrokeEnd().y - this.firstPoint().y;
// this is based on a static computation in mark.renderer
// if you change it there, change it here
this.position.z = mark.position.z + ( mark.maxTime / 50 );
}
};
this.positionToStart = function () {
this.position.x = 0;
this.position.y = 0;
this.position.z = 0;
};
this.init();
};
return mark;
}( Mark || {} ) );

56
ffdemo/static/assets/js/vendor/mark/mark.gmlPoint.js поставляемый Normal file
Просмотреть файл

@ -0,0 +1,56 @@
var Mark = ( function ( mark ) {
mark.gmlPoint = function( x, y, time, speed, z ) {
this.x = x;
this.y = y;
this.z = typeof z == "integer" ? z : 0;
this.time = time;
this.speed = speed;
this.angle = 0;
this.significance = 1; // 1-5 value for how significant this point is -- useful for easign the complexity of lines that are far from the camera
// returns the distance between this and another point
this.distanceToPoint = function( point ) {
return Math.sqrt( Math.pow( (point.x - this.x ), 2 ) + Math.pow( (point.y - this.y ), 2 ) );
};
// returns a speed between this point and a point in the future
this.speedToPoint = function( point ) {
var dp = this.distanceToPoint( point );
var dt = point.time - this.time;
return dp / dt;
};
// ensures this point's speed is not changing substantially faster than it should
// allowance is a per unit distance change
this.smoothAgainst = function( point, allowance ) {
var d = this.distanceToPoint( point );
var a = allowance * d;
if ( Math.abs( this.speed - point.speed ) > a ) {
this.speed = this.speed > point.speed ? point.speed + a : point.speed - a;
}
};
// considers a prior point and sets this point's angle accordingly
// ensures that the angle is a radian value between 0 and 2 * PI
this.setAngleFromPoint = function ( point ) {
this.angle = Math.atan2( point.y - this.y, point.x - this.x ) + ( Math.PI / 2 );
this.angle = this.angle % ( 2 * Math.PI );
if( this.angle < 0 ) this.angle = ( 2 * Math.PI ) + this.angle;
};
// returns a basic copy of this point
this.clone = function () {
return { x: this.x, y: this.y, z: this.z, time: this.time, significance: this.significance, angle: this.angle, speed: this.speed };
};
// returns a copy of this point translated by x, y
this.getTranslatedPoint = function ( x, y ) {
var point = this.clone();
point.x += x;
point.y += y;
return point;
};
};
return mark;
}( Mark || {} ) );

45
ffdemo/static/assets/js/vendor/mark/mark.layer.js поставляемый Normal file
Просмотреть файл

@ -0,0 +1,45 @@
var Mark = ( function ( mark ) {
mark.layer = function( manager, name ) {
this.canvas = null;
this.context = null;
this.dirtyRectangles = [];
this.layerName = name;
this.manager = manager;
this.clean = function() {
if( this.dirtyRectangles.length == 0 ) {
// if theres no dirtyRectangles, clear the whole thing (probably not the best default)
this.context.clearRect( 0, 0, this.canvas.width, this.canvas.height );
} else {
// loop through dirty rectangles, and run clearRect on each
for( var i = 0; i< this.dirtyRectangles.length; i++ ) {
var rect = this.dirtyRectangles[i];
this.context.clearRect( i.x, i.y, i.w, i.h );
}
}
};
this.setSize = function( w, h ) {
if( this.canvas.width != w ) this.canvas.width = w;
if( this.canvas.height != h ) this.canvas.height = h;
};
this.init = function () {
this.canvas = document.createElement( 'canvas' );
this.context = this.canvas.getContext( '2d' );
this.setSize( this.manager.container.scrollWidth, this.manager.container.scrollHeight );
this.manager.layerWrapper.appendChild( this.canvas );
};
this.remove = function () {
this.manager.layerWrapper.removeChild( this.canvas );
};
this.init();
};
return mark;
}( Mark || {} ) );

42
ffdemo/static/assets/js/vendor/mark/mark.layerManager.js поставляемый Normal file
Просмотреть файл

@ -0,0 +1,42 @@
var Mark = ( function ( mark ) {
mark.layerManager = function( container ) {
this.container = container;
this.layerWrapper = null;
this.layers = {};
this.init = function () {
// attach a new div for our layers to reside in
this.layerWrapper = document.createElement( 'div' );
this.layerWrapper.className = 'mark-layerManager';
this.container.appendChild( this.layerWrapper );
};
// add a layer to our container
this.addLayer = function( name ) {
var layer = new mark.layer( this, name );
this.layers[name] = layer;
return layer;
};
// remove all layers from our container
this.removeAll = function () {
for( var layer in this.layers ) {
this.layers[layer].remove();
delete this.layers[layer];
}
};
// resize all layers to the passed width and height
this.resizeAll = function( w, h ) {
for( var layer in this.layers ) {
this.layers[layer].setSize( w, h );
}
};
this.init();
};
return mark;
}( Mark || {} ) );

156
ffdemo/static/assets/js/vendor/mark/mark.renderer.js поставляемый Normal file
Просмотреть файл

@ -0,0 +1,156 @@
var Mark = ( function ( mark ) {
mark.renderer = {
// all points are passed through here durring rendering
// current supported options
// - offset - an object with x, y, and possibly z
// - w - output width - defaults to 500
// - h - output height - defaults to 500
// - mode - which of the translate modes we should use. defaults to 'pinhole'
// - scale - an object with x, y and possibly thickness values to scale the x, y coords and the stroke thickness by.
translatePoint: function( point, options ) {
var modes = {
// simple offset and scale translate
'flatScale': function( rP, options ) {
var offsetX = ('offset' in options) && ( 'x' in options['offset'] ) ? options.offset.x : 0;
var offsetY = ('offset' in options) && ( 'y' in options['offset'] ) ? options.offset.y : 0;
var scaleX = ('scale' in options) && ( 'x' in options['scale'] ) ? options.scale.x : 1;
var scaleY = ('scale' in options) && ( 'y' in options['scale'] ) ? options.scale.y : 1;
rP.x *= scaleX;
rP.y *= scaleY;
rP.x += offsetX;
rP.y += offsetY;
return rP;
},
// maps the point to a 3D parabolic shape and then uses a pinhole camera approace to map it back to 2D coords
'pinhole': function( rP, options ) {
var offsetX = ('offset' in options) && ( 'x' in options['offset'] ) ? options.offset.x : 0;
var offsetY = ('offset' in options) && ( 'y' in options['offset'] ) ? options.offset.y : 0;
var offsetZ = ('offset' in options) && ( 'z' in options['offset'] ) ? options.offset.z : 0;
var w = ('w' in options) ? options.w : 500;
var h = ('h' in options) ? options.h : 500;
var spread = 100;
var shift = -100;
rP.x += offsetX;
rP.y += offsetY;
if( rP.x > 0 ) {
rP.z = Math.pow( ( ( rP.x - shift ) / spread ), 2) + offsetZ;
} else {
rP.z = Math.pow( ( ( rP.x - shift ) / spread / 2 ), 2) + offsetZ;
}
// make it a bit deeper based on time
if( rP.time ) rP.z += rP.time / 50;
var v = 2 / ( rP.z );
rP.x = rP.x * v * ( h / 2 ) + ( w / 2 );
rP.y = rP.y * v * ( h / 2 ) + ( h / 2 );
return rP;
}
};
// create a new object based on our point. Basically this will be a clone of the gmlPoint w/o functions
var rP = { x: point.x, y: point.y, z: point.z, time: point.time, significance: point.significance, angle: point.angle, speed: point.speed };
// run the appropriate function and return the result
return ( 'mode' in options ) && ( options['mode'] in modes ) ?
modes[options['mode']]( rP, options ) : modes['pinhole']( rP, options );
},
// returns a subset of strokes for the given time
strokesAtTime: function ( strokes, time ) {
// if( time > this.maxTime ) return this.strokes;
var sat = [[]];
var curIndex = [0, 0];
var nextPoint = strokes[curIndex[0]][curIndex[1]];
while( nextPoint.time < time ) {
sat[sat.length - 1].push( nextPoint );
curIndex[1]++;
if( strokes[curIndex[0]].length == curIndex[1] ) {
// next stroke if we've got another
if( strokes.length == curIndex[0] + 1 ) break;
curIndex[0]++;
curIndex[1] = 0;
sat.push([]);
}
nextPoint = strokes[curIndex[0]][curIndex[1]];
}
return sat;
},
// renders a scene for the viz
// accepted options - cursor { x, y }, width, height
renderScene: function( scene, options ) {
var lastMarkStart;
var w = options.width;
var h = options.height;
for( var i = scene.objects.length - 1; i >= 0; i-- ) {
// set our offset based on the camera and the marks position
var offset = {
x: scene.objects[i].position.x - scene.camera.position.x,
y: scene.objects[i].position.y - scene.camera.position.y,
z: scene.objects[i].position.z - scene.camera.position.z
};
colorBase = scene.objects[i].color;
var translateOptions = {
offset: offset,
w: options.width,
h: options.height,
mode: 'pinhole'
};
if( i < scene.objects.length - 1 && !( scene.objects[i].reference in scene.timers ) ) {
// draw the connection
var offset1 = {
x: scene.objects[i+1].position.x - scene.camera.position.x,
y: scene.objects[i+1].position.y - scene.camera.position.y,
z: scene.objects[i+1].position.z - scene.camera.position.z
};
var offset2 = {
x: scene.objects[i].position.x - scene.camera.position.x,
y: scene.objects[i].position.y - scene.camera.position.y,
z: scene.objects[i].position.z - scene.camera.position.z
};
var p1 = scene.objects[i+1].leftmostStrokeStart();
var p2 = scene.objects[i].rightmostStrokeEnd();
Mark.connectionBrush( scene.canvasContext , p1, p2, offset1, offset2, w, h );
}
var translateOptions = {
offset: offset,
w: options.width,
h: options.height,
mode: 'pinhole'
};
// DRAW THAT MARK
if( scene.objects[i].reference in scene.timers ) {
// render a mark that is currently being replayed
var strokes = mark.renderer.strokesAtTime(
scene.objects[i].strokes,
( ( new Date() ).getTime() - scene.timers[scene.objects[i].reference].start ) * scene.timers[scene.objects[i].reference].speed );
if( strokes && strokes.length > 0 && strokes[0].length > 0 )
scene.objects[i].renderedBounds =
Mark.thickMarkBrush( scene.canvasContext, strokes, translateOptions, colorBase, options.width, options.height );
} else {
// render a mark that is not being played back
scene.objects[i].renderedBounds =
Mark.thickMarkBrush( scene.canvasContext, scene.objects[i].strokes, translateOptions, colorBase, options.width, options.height );
}
}
},
// simpler render method for playing back an individual mark using our flatScale translate method
// accepted options - timer, width, height, offset {x, y}, scale, color
renderMark: function( canvasContext, aMark, options ) {
// DRAW THAT MARK
var translateOptions = {
offset: options.offset,
scale: options.scale,
mode: 'flatScale'
};
if( 'timer' in options && options.timer ) {
// render a mark that is currently being replayed
var strokes = mark.renderer.strokesAtTime( aMark.strokes, ( ( new Date() ).getTime() - options.timer.start ) * options.timer.speed );
if( strokes && strokes.length > 0 && strokes[0].length > 0 )
aMark.renderedBounds = Mark.thickMarkBrush( canvasContext, strokes, translateOptions, options.color );
} else {
// render a mark that is not being played back
aMark.renderedBounds = Mark.thickMarkBrush( canvasContext, aMark.strokes, translateOptions, options.color );
}
}
};
return mark;
}( Mark || {} ) );

37
ffdemo/static/assets/js/vendor/mark/mark.scene.js поставляемый Normal file
Просмотреть файл

@ -0,0 +1,37 @@
var Mark = ( function ( mark ) {
mark.scene = function ( ) {
this.camera = new Mark.camera();
this.objects = [];
this.canvasContext = null;
this.timers = {};
this.init = function ( ) {
};
this.addObject = function ( object ) {
this.objects.push( object );
};
this.removeObject = function ( index ) {
this.objects.splice( index, 1 );
};
this.update = function ( ) {
var now = ( new Date() ).getTime();
for( mark in this.timers ) {
if( this.timers[mark].end < now ) {
delete this.timers[mark];
}
}
};
this.init();
};
return mark;
}( Mark || {} ) );

86
ffdemo/static/assets/js/vendor/mark/mark.simplifyPath.js поставляемый Normal file
Просмотреть файл

@ -0,0 +1,86 @@
var Mark = ( function ( mark ) {
mark.simplification = {
// helper function
Line: function( p1, p2 ) {
this.p1 = p1;
this.p2 = p2;
this.distanceToPoint = function( point ) {
// slope
var m = ( this.p2.y - this.p1.y ) / ( this.p2.x - this.p1.x );
// y offset
var b = this.p1.y - ( m * this.p1.x );
var d = [];
// distance to the linear equation
d.push( Math.abs( point.y - ( m * point.x ) - b ) / Math.sqrt( Math.pow( m, 2 ) + 1 ) )
// distance to p1
d.push( Math.sqrt( Math.pow( ( point.x - this.p1.x ), 2 ) + Math.pow( ( point.y - this.p1.y ), 2 ) ) )
// distance to p2
d.push( Math.sqrt( Math.pow( ( point.x - this.p2.x ), 2 ) + Math.pow( ( point.y - this.p2.y ), 2 ) ) );
// return the smallest distance
return d.sort( function( a, b ) {
return ( a - b ) //causes an array to be sorted numerically and ascending
} )[0];
}
},
// main simplification algorithm
douglasPeucker: function( points, tolerance ) {
var returnPoints = [];
if ( points.length <= 2 ) {
return [points[0]];
}
// make line from start to end
var line = new mark.simplification.Line( points[0], points[points.length - 1] );
// find the largest distance from intermediate poitns to this line
var maxDistance = 0;
var maxDistanceIndex = 0;
for( var i = 1; i <= points.length - 2; i++ ) {
var distance = line.distanceToPoint( points[ i ] );
if( distance > maxDistance ) {
maxDistance = distance;
maxDistanceIndex = i;
}
}
// check if the max distance is greater than our tollerance allows
if ( maxDistance >= tolerance ) {
var p = points[maxDistanceIndex];
line.distanceToPoint( p, true );
// include this point in the output
returnPoints = returnPoints.concat( mark.simplification.douglasPeucker( points.slice( 0, maxDistanceIndex + 1 ), tolerance ) );
// returnPoints.push( points[maxDistanceIndex] );
returnPoints = returnPoints.concat( mark.simplification.douglasPeucker( points.slice( maxDistanceIndex, points.length ), tolerance ) );
} else {
// ditching this point
var p = points[maxDistanceIndex];
line.distanceToPoint( p, true );
returnPoints = [points[0]];
}
return returnPoints;
},
// returns a simplified version of a stroke based on the passed tolerance
simplifyPath: function( points, tolerance ) {
var arr = mark.simplification.douglasPeucker( points, tolerance );
// always have to push the very last point on so it doesn't get left off
arr.push( points[points.length - 1 ] );
return arr;
},
// weights a stroke based on the passed tolerances
weightPath: function( points, tolerances ) {
var maxSignificance = tolerances.length + 1;
var tmpPoints = points;
while( tolerance = tolerances.shift() ) {
// limit the points array by the current tolerance
tmpPoints = mark.simplification.douglasPeucker( tmpPoints, tolerance );
// increment the significance of all points that pass the simplfification
for( var i = 0; i < tmpPoints.length; i++ ) {
tmpPoints[i].significance++;
}
}
// maker sure the beginning and end of all strokes get the hightest significance
points[0].significance = maxSignificance;
points[points.length - 1].significance = maxSignificance;
return points;
}
};
return mark;
}( Mark || {} ) );

110
ffdemo/static/assets/js/vendor/plugins/sammy.hash_push_proxy.js поставляемый Normal file
Просмотреть файл

@ -0,0 +1,110 @@
Sammy.HashPushProxy = function(app, run_interval_every) {
this.app = app;
// detect if we can use the history api
this.supportsHistory = !!( window.history && history.pushState );
if( !this.supportsHistory ) {
// if history is not supported, start polling
this._startPolling( run_interval_every );
// and set this as a non native app?
this.is_native = false;
}
};
Sammy.HashPushProxy.prototype = {
// bind the proxy events to the current app.
bind: function() {
var proxy = this, app = this.app;
if( this.app.supportsHistory ) {
$( window ).bind( 'popstate', function( e ) {
proxy.app.trigger( 'location-changed' );
} );
$( 'a' ).live( 'click', function(e) {
// Do not bind external links
if ( location.hostname == this.hostname ) {
e.preventDefault();
// if the history API is supported
if ( proxy.historyAPISupported ) {
proxy.setLocation( $( this ).attr( 'href' ) );
proxy.app.trigger( 'location-changed' );
} else {
proxy.setLocation( '#' + $( this ).attr( 'href' ) );
proxy.app.trigger( 'location-changed' );
}
}
} );
} else {
$( window ).bind( 'hashchange.' + this.app.eventNamespace(), function( e, non_native ) {
// if we receive a native hash change event, set the proxy accordingly
// and stop polling
if ( proxy.is_native === false && !non_native ) {
Sammy.log('native hash change exists, using');
proxy.is_native = true;
window.clearInterval(Sammy.HashLocationProxy._interval);
}
app.trigger('location-changed');
});
if (!Sammy.HashLocationProxy._bindings) {
Sammy.HashLocationProxy._bindings = 0;
}
Sammy.HashLocationProxy._bindings++;
}
},
// unbind the proxy events from the current app
unbind: function() {
if( this.app.supportsHistory ) {
$('a').unbind('click');
$(window).unbind('popstate');
} else {
$(window).unbind('hashchange.' + this.app.eventNamespace());
Sammy.HashLocationProxy._bindings--;
if (Sammy.HashLocationProxy._bindings <= 0) {
window.clearInterval(Sammy.HashLocationProxy._interval);
}
}
},
// get the current location.
getLocation: function() {
if( this.app.supportsHistory ) {
return window.location.pathname;
} else {
// Bypass the `window.location.hash` attribute. If a question mark
// appears in the hash IE6 will strip it and all of the following
// characters from `window.location.hash`.
var matches = window.location.toString().match(/^[^#]*(#.+)$/);
return matches ? matches[1] : '';
}
},
// set the current location to `new_location`
setLocation: function( new_location ) {
if( this.app.supportsHistory ) {
history.pushState( { path: this.path }, '', new_location )
} else {
return (window.location = new_location);
}
},
_startPolling: function(every) {
// set up interval
var proxy = this;
if (!Sammy.HashLocationProxy._interval) {
if (!every) { every = 10; }
var hashCheck = function() {
var current_location = proxy.getLocation();
if (!Sammy.HashLocationProxy._last_location ||
current_location != Sammy.HashLocationProxy._last_location) {
window.setTimeout(function() {
$(window).trigger('hashchange', [true]);
}, 13);
}
Sammy.HashLocationProxy._last_location = current_location;
};
hashCheck();
Sammy.HashLocationProxy._interval = window.setInterval(hashCheck, every);
}
}
};

141
ffdemo/static/assets/js/vendor/plugins/sammy.template.js поставляемый Executable file
Просмотреть файл

@ -0,0 +1,141 @@
(function($) {
// Simple JavaScript Templating
// John Resig - http://ejohn.org/ - MIT Licensed
// adapted from: http://ejohn.org/blog/javascript-micro-templating/
// originally $.srender by Greg Borenstein http://ideasfordozens.com in Feb 2009
// modified for Sammy by Aaron Quint for caching templates by name
var srender_cache = {};
var srender = function(name, template, data, options) {
var fn, escaped_string;
// target is an optional element; if provided, the result will be inserted into it
// otherwise the result will simply be returned to the caller
if (srender_cache[name]) {
fn = srender_cache[name];
} else {
if (typeof template == 'undefined') {
// was a cache check, return false
return false;
}
// If options escape_html is false, dont escape the contents by default
if (options && options.escape_html === false) {
escaped_string = "\",$1,\"";
} else {
escaped_string = "\",h($1),\"";
}
// Generate a reusable function that will serve as a template
// generator (and which will be cached).
fn = srender_cache[name] = new Function("obj",
"var ___$$$___=[],print=function(){___$$$___.push.apply(___$$$___,arguments);};" +
// Introduce the data as local variables using with(){}
"with(obj){___$$$___.push(\"" +
// Convert the template into pure JavaScript
String(template)
.replace(/[\r\t\n]/g, " ")
.replace(/\"/g, '\\"')
.split("<%").join("\t")
.replace(/((^|%>)[^\t]*)/g, "$1\r")
.replace(/\t=(.*?)%>/g, escaped_string)
.replace(/\t!(.*?)%>/g, "\",$1,\"")
.split("\t").join("\");")
.split("%>").join("___$$$___.push(\"")
.split("\r").join("")
+ "\");}return ___$$$___.join('');");
}
if (typeof data != 'undefined') {
return fn(data);
} else {
return fn;
}
};
Sammy = Sammy || {};
// `Sammy.Template` is a simple plugin that provides a way to create
// and render client side templates. The rendering code is based on John Resig's
// quick templates and Greg Borenstien's srender plugin.
// This is also a great template/boilerplate for Sammy plugins.
//
// Templates use `<% %>` tags to denote embedded javascript.
//
// ### Examples
//
// Here is an example template (user.template):
//
// // user.template
// <div class="user">
// <div class="user-name"><%= user.name %></div>
// <% if (user.photo_url) { %>
// <div class="photo"><img src="<%= user.photo_url %>" /></div>
// <% } %>
// </div>
//
// Given that is a publicly accesible file, you would render it like:
//
// // app.js
// $.sammy(function() {
// // include the plugin
// this.use('Template');
//
// this.get('#/', function() {
// // the template is rendered in the current context.
// this.user = {name: 'Aaron Quint'};
// // partial calls template() because of the file extension
// this.partial('user.template');
// })
// });
//
// You can also pass a second argument to use() that will alias the template
// method and therefore allow you to use a different extension for template files
// in <tt>partial()</tt>
//
// // alias to 'tpl'
// this.use(Sammy.Template, 'tpl');
//
// // now .tpl files will be run through srender
// this.get('#/', function() {
// this.partial('myfile.tpl');
// });
//
// By default, the data passed into the tempalate is passed automatically passed through
// Sammy's `escapeHTML` method in order to prevent possible XSS attacks. This is
// a problem though if you're using something like `Sammy.Form` which renders HTML
// within the templates. You can get around this in two ways. One, you can use the
// `<%! %>` instead of `<%= %>`. Two, you can pass the `escape_html = false` option
// when interpolating, i.e:
//
// this.get('#/', function() {
// this.template('myform.tpl', {form: "<form></form>"}, {escape_html: false});
// });
//
Sammy.Template = function(app, method_alias) {
// *Helper:* Uses simple templating to parse ERB like templates.
//
// ### Arguments
//
// * `template` A String template. '<% %>' tags are evaluated as Javascript and replaced with the elements in data.
// * `data` An Object containing the replacement values for the template.
// data is extended with the <tt>EventContext</tt> allowing you to call its methods within the template.
// * `name` An optional String name to cache the template.
//
var template = function(template, data, name, options) {
// use name for caching
if (typeof name == 'undefined') { name = template; }
if (typeof options == 'undefined' && typeof name == 'object') {
options = name; name = template;
}
return srender(name, template, $.extend({}, this, data), options);
};
// set the default method name/extension
if (!method_alias) { method_alias = 'template'; }
// create the helper at the method alias
app.helper(method_alias, template);
};
})(jQuery);

1829
ffdemo/static/assets/js/vendor/sammy-0.6.3.js поставляемый Executable file

Разница между файлами не показана из-за своего большого размера Загрузить разницу

470
ffdemo/static/assets/js/vendor/tween.js поставляемый Normal file
Просмотреть файл

@ -0,0 +1,470 @@
/**
* @author sole / http://soledadpenades.com
* @author mr.doob / http://mrdoob.com
* @author Robert Eisele / http://www.xarg.org
* @author Philippe / http://philippe.elsass.me
* @author Robert Penner / http://www.robertpenner.com/easing_terms_of_use.html
*/
var TWEEN = TWEEN || ( function() {
var i, n, time, tweens = [];
this.add = function ( tween ) {
tweens.push( tween );
};
this.remove = function ( tween ) {
i = tweens.indexOf( tween );
if ( i !== -1 ) {
tweens.splice( i, 1 );
}
};
this.update = function () {
i = 0;
n = tweens.length;
time = new Date().getTime();
while ( i < n ) {
if ( tweens[ i ].update( time ) ) {
i++;
} else {
tweens.splice( i, 1 );
n--;
}
}
};
return this;
} )();
TWEEN.Tween = function ( object ) {
var _object = object,
_valuesStart = {},
_valuesDelta = {},
_valuesEnd = {},
_duration = 1000,
_delayTime = 0,
_startTime = null,
_easingFunction = TWEEN.Easing.Linear.EaseNone,
_chainedTween = null,
_onUpdateCallback = null,
_onCompleteCallback = null;
this.to = function ( properties, duration ) {
if( duration !== null ) {
_duration = duration;
}
for ( var property in properties ) {
// This prevents the engine from interpolating null values
if ( _object[ property ] === null ) {
continue;
}
// The current values are read when the tween starts;
// here we only store the final desired values
_valuesEnd[ property ] = properties[ property ];
}
return this;
};
this.start = function () {
TWEEN.add( this );
_startTime = new Date().getTime() + _delayTime;
for ( var property in _valuesEnd ) {
// Again, prevent dealing with null values
if ( _object[ property ] === null ) {
continue;
}
_valuesStart[ property ] = _object[ property ];
_valuesDelta[ property ] = _valuesEnd[ property ] - _object[ property ];
}
return this;
};
this.stop = function () {
TWEEN.remove( this );
return this;
};
this.delay = function ( amount ) {
_delayTime = amount;
return this;
};
this.easing = function ( easing ) {
_easingFunction = easing;
return this;
};
this.chain = function ( chainedTween ) {
_chainedTween = chainedTween;
};
this.onUpdate = function ( onUpdateCallback ) {
_onUpdateCallback = onUpdateCallback;
return this;
};
this.onComplete = function ( onCompleteCallback ) {
_onCompleteCallback = onCompleteCallback;
return this;
};
this.update = function ( time ) {
var property, elapsed, value;
if ( time < _startTime ) {
return true;
}
elapsed = ( time - _startTime ) / _duration;
elapsed = elapsed > 1 ? 1 : elapsed;
value = _easingFunction( elapsed );
for ( property in _valuesDelta ) {
_object[ property ] = _valuesStart[ property ] + _valuesDelta[ property ] * value;
}
if ( _onUpdateCallback !== null ) {
_onUpdateCallback.call( _object, value );
}
if ( elapsed == 1 ) {
if ( _onCompleteCallback !== null ) {
_onCompleteCallback.call( _object );
}
if ( _chainedTween !== null ) {
_chainedTween.start();
}
return false;
}
return true;
};
/*
this.destroy = function () {
TWEEN.remove( this );
};
*/
}
TWEEN.Easing = { Linear: {}, Quadratic: {}, Cubic: {}, Quartic: {}, Quintic: {}, Sinusoidal: {}, Exponential: {}, Circular: {}, Elastic: {}, Back: {}, Bounce: {} };
TWEEN.Easing.Linear.EaseNone = function ( k ) {
return k;
};
//
TWEEN.Easing.Quadratic.EaseIn = function ( k ) {
return k * k;
};
TWEEN.Easing.Quadratic.EaseOut = function ( k ) {
return - k * ( k - 2 );
};
TWEEN.Easing.Quadratic.EaseInOut = function ( k ) {
if ( ( k *= 2 ) < 1 ) return 0.5 * k * k;
return - 0.5 * ( --k * ( k - 2 ) - 1 );
};
//
TWEEN.Easing.Cubic.EaseIn = function ( k ) {
return k * k * k;
};
TWEEN.Easing.Cubic.EaseOut = function ( k ) {
return --k * k * k + 1;
};
TWEEN.Easing.Cubic.EaseInOut = function ( k ) {
if ( ( k *= 2 ) < 1 ) return 0.5 * k * k * k;
return 0.5 * ( ( k -= 2 ) * k * k + 2 );
};
//
TWEEN.Easing.Quartic.EaseIn = function ( k ) {
return k * k * k * k;
};
TWEEN.Easing.Quartic.EaseOut = function ( k ) {
return - ( --k * k * k * k - 1 );
}
TWEEN.Easing.Quartic.EaseInOut = function ( k ) {
if ( ( k *= 2 ) < 1) return 0.5 * k * k * k * k;
return - 0.5 * ( ( k -= 2 ) * k * k * k - 2 );
};
//
TWEEN.Easing.Quintic.EaseIn = function ( k ) {
return k * k * k * k * k;
};
TWEEN.Easing.Quintic.EaseOut = function ( k ) {
return ( k = k - 1 ) * k * k * k * k + 1;
};
TWEEN.Easing.Quintic.EaseInOut = function ( k ) {
if ( ( k *= 2 ) < 1 ) return 0.5 * k * k * k * k * k;
return 0.5 * ( ( k -= 2 ) * k * k * k * k + 2 );
};
//
TWEEN.Easing.Sinusoidal.EaseIn = function ( k ) {
return - Math.cos( k * Math.PI / 2 ) + 1;
};
TWEEN.Easing.Sinusoidal.EaseOut = function ( k ) {
return Math.sin( k * Math.PI / 2 );
};
TWEEN.Easing.Sinusoidal.EaseInOut = function ( k ) {
return - 0.5 * ( Math.cos( Math.PI * k ) - 1 );
};
//
TWEEN.Easing.Exponential.EaseIn = function ( k ) {
return k == 0 ? 0 : Math.pow( 2, 10 * ( k - 1 ) );
};
TWEEN.Easing.Exponential.EaseOut = function ( k ) {
return k == 1 ? 1 : - Math.pow( 2, - 10 * k ) + 1;
};
TWEEN.Easing.Exponential.EaseInOut = function ( k ) {
if ( k == 0 ) return 0;
if ( k == 1 ) return 1;
if ( ( k *= 2 ) < 1 ) return 0.5 * Math.pow( 2, 10 * ( k - 1 ) );
return 0.5 * ( - Math.pow( 2, - 10 * ( k - 1 ) ) + 2 );
};
//
TWEEN.Easing.Circular.EaseIn = function ( k ) {
return - ( Math.sqrt( 1 - k * k ) - 1);
};
TWEEN.Easing.Circular.EaseOut = function ( k ) {
return Math.sqrt( 1 - --k * k );
};
TWEEN.Easing.Circular.EaseInOut = function ( k ) {
if ( ( k /= 0.5 ) < 1) return - 0.5 * ( Math.sqrt( 1 - k * k) - 1);
return 0.5 * ( Math.sqrt( 1 - ( k -= 2) * k) + 1);
};
//
TWEEN.Easing.Elastic.EaseIn = function( k ) {
var s, a = 0.1, p = 0.4;
if ( k == 0 ) return 0; if ( k == 1 ) return 1; if ( !p ) p = 0.3;
if ( !a || a < 1 ) { a = 1; s = p / 4; }
else s = p / ( 2 * Math.PI ) * Math.asin( 1 / a );
return - ( a * Math.pow( 2, 10 * ( k -= 1 ) ) * Math.sin( ( k - s ) * ( 2 * Math.PI ) / p ) );
};
TWEEN.Easing.Elastic.EaseOut = function( k ) {
var s, a = 0.1, p = 0.4;
if ( k == 0 ) return 0; if ( k == 1 ) return 1; if ( !p ) p = 0.3;
if ( !a || a < 1 ) { a = 1; s = p / 4; }
else s = p / ( 2 * Math.PI ) * Math.asin( 1 / a );
return ( a * Math.pow( 2, - 10 * k) * Math.sin( ( k - s ) * ( 2 * Math.PI ) / p ) + 1 );
};
TWEEN.Easing.Elastic.EaseInOut = function( k ) {
var s, a = 0.1, p = 0.4;
if ( k == 0 ) return 0; if ( k == 1 ) return 1; if ( !p ) p = 0.3;
if ( !a || a < 1 ) { a = 1; s = p / 4; }
else s = p / ( 2 * Math.PI ) * Math.asin( 1 / a );
if ( ( k *= 2 ) < 1 ) return - 0.5 * ( a * Math.pow( 2, 10 * ( k -= 1 ) ) * Math.sin( ( k - s ) * ( 2 * Math.PI ) / p ) );
return a * Math.pow( 2, -10 * ( k -= 1 ) ) * Math.sin( ( k - s ) * ( 2 * Math.PI ) / p ) * 0.5 + 1;
};
//
TWEEN.Easing.Back.EaseIn = function( k ) {
var s = 1.70158;
return k * k * ( ( s + 1 ) * k - s );
};
TWEEN.Easing.Back.EaseOut = function( k ) {
var s = 1.70158;
return ( k = k - 1 ) * k * ( ( s + 1 ) * k + s ) + 1;
};
TWEEN.Easing.Back.EaseInOut = function( k ) {
var s = 1.70158 * 1.525;
if ( ( k *= 2 ) < 1 ) return 0.5 * ( k * k * ( ( s + 1 ) * k - s ) );
return 0.5 * ( ( k -= 2 ) * k * ( ( s + 1 ) * k + s ) + 2 );
};
//
TWEEN.Easing.Bounce.EaseIn = function( k ) {
return 1 - TWEEN.Easing.Bounce.EaseOut( 1 - k );
};
TWEEN.Easing.Bounce.EaseOut = function( k ) {
if ( ( k /= 1 ) < ( 1 / 2.75 ) ) {
return 7.5625 * k * k;
} else if ( k < ( 2 / 2.75 ) ) {
return 7.5625 * ( k -= ( 1.5 / 2.75 ) ) * k + 0.75;
} else if ( k < ( 2.5 / 2.75 ) ) {
return 7.5625 * ( k -= ( 2.25 / 2.75 ) ) * k + 0.9375;
} else {
return 7.5625 * ( k -= ( 2.625 / 2.75 ) ) * k + 0.984375;
}
};
TWEEN.Easing.Bounce.EaseInOut = function( k ) {
if ( k < 0.5 ) return TWEEN.Easing.Bounce.EaseIn( k * 2 ) * 0.5;
return TWEEN.Easing.Bounce.EaseOut( k * 2 - 1 ) * 0.5 + 0.5;
};

Просмотреть файл

@ -0,0 +1,31 @@
{% extends "base.html" %}
{% load i18n %}
{% block bodytag %}
<body class="behind-the-code about">
{% endblock %}
{% block content %}
<section id="static-content">
<section class="col-secondary">
<nav id="about-nav">
<ul>
<li id="about-mozilla"><a href="/about/mozilla">{{ _('About Mozilla') }}</a></li>
<li id="about-gml"><a href="/about/gml">{{ _('GML') }}</a></li>
<li id="about-evan-roth"><a href="/about/evan-roth">{{ _('Evan Roth') }}</a></li>
<li id="about-code"><a href="/about/code">{{ _('Code Repository') }}</a></li>
</ul>
</nav>
</section>
<section class="col-main col-last">
{% block about-content %}
<h1>{{ _('Behind the Code') }}</h1>
{% blocktrans %}
<p>A non-profit browser dedicated to placing the future of the Web in the hands of the people. An artist inspired by graffiti and free culture. What happens when these two like-minded entities collaborate? Mark Up, a project that reflects the collective nature of the Web.</p>
<p>Heres where youll find more information on Mozilla and Evan Roth, and the project born of this collaboration.</p>
<p>Learn more about the mission of Mark Up and the contribution from respected open Web advocate Lawrence Lessig <a href="/manifesto">here</a>.</p>
{% endblocktrans %}
{% endblock %}
</section>
</section>
{% endblock %}

Просмотреть файл

@ -0,0 +1,103 @@
{% load i18n %}
{% load localeurl_tags %}
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="title" content="Mozilla Firefox Mark Up">
<meta name="description" content="The Web is your creation. Show your support to keep it open and accessible to all. Make your mark." />
<link rel="image_src" href="/media/assets/images/site_thumb.jpg" />
<meta property="og:title" content="Mozilla Firefox Mark Up"/>
<meta property="og:type" content="website"/>
<meta property="og:url" content="http://markup.mozilla.com"/>
<meta property="og:image" content="http://markup.mozilla.com/media/assets/images/site_thumb.jpg"/>
<meta property="og:site_name" content="Mozilla Firefox Mark Up"/>
<meta property="og:description" content="The Web is your creation. And, Mark Up is an open invitation to show your support for keeping the Web public and accessible to all. Make your mark."/>
<title>{{ _('Mozilla Firefox Mark Up') }}</title>
<link rel="shortcut icon" href="/media/assets/images/favicon.png" />
<link href="/media/assets/css/style.css" type="text/css" rel="stylesheet" />
<script src="/media/assets/js/jquery-1.5.1.min.js" type="text/javascript"></script>
<script src="/media/assets/js/common.js" type="text/javascript"></script>
{% block js %}{% endblock %}
</head>
{% block bodytag %}
<body>
{% endblock %}
<header>
<section id="main-links">
<h2><a href="http://www.mozilla.org/" target="_blank" id="mozilla-logo">{{ _('Mozilla') }}</a></h2>
<h1><a href="/">{{ _('Mozilla Mark Up') }}</a></h1>
</section>
</header>
<section id="main-navigation">
<nav>
<div id="coming-soon-tip">{{ _('Coming soon') }}</div>
<ul>
<li id="manifesto"><a href="/manifesto">{{ _('Manifesto') }}</a></li>
<li id="make-your-mark"><a href="/#/mark/new">{{ _('Make Your Mark') }}</a></li>
<li id="behind-the-code"><a href="/about/">{{ _('Behind The Code') }}</a></li>
<li id="community" class="disabled">{{ _('Community') }}</li>
<li class="download-link"><a href="http://www.mozilla.com/firefox" target="_blank">{{ _('Download Firefox 4') }}</a></li>
</ul>
</nav>
</section>
<section id="content">
{% block content %}{% endblock %}
</section>
<footer>
<section id="callout-boxes">
<div id="callout-boxes-wrapper">
<div class="callout-box">
<a href="/about"><img src="/media/assets/images/evan_roth.jpg" width="57px" height="57px" alt="{{ _('Evan Roth') }}" /></a>
<h3><a href="/about">{{ _('Collaborating with Mark Up') }}</a></h3>
<p>{% blocktrans %}Learn more about our collaboration with artist Evan Roth and Lawrence Lessig's contribution.{% endblocktrans %}</p>
</div>
<div class="callout-box">
<a href="/newsletter"><img src="/media/assets/images/world_eater.jpg" width="57px" height="57px" alt="{{ _('Mozilla Newsletter') }}" /></a>
<h3><a href="/newsletter">{{ _('Read all about it') }}</a></h3>
<p>{% blocktrans %}Subscribe to Mozilla's monthly email newsletter and receive the latest tips and tricks for getting the most out of Firefox.{% endblocktrans %}</p>
</div>
<div class="callout-box">
<a href="https://demos.mozilla.org/" target="_blank"><img src="/media/assets/images/web_of_wonders.jpg" width="57px" height="57px" alt="{{ _('Web Of Wonders') }}" /></a>
<h3><a href="https://demos.mozilla.org/" target="_blank">{{ _('Web O\'(pen) Wonder') }}</a></h3>
<p>{% blocktrans %}Want to see what the Web is really capable of? Explore the Web O' Wonder demo gallery and prepare to be amazed!{% endblocktrans %}</p>
</div>
<div class="callout-box last">
<a href="http://www.mozilla.com/firefox" target="_blank"><img src="/media/assets/images/firefox_logo.jpg" width="57px" height="57px" alt="{{ _('Download Firefox 4') }}" /></a>
<h3><a href="http://www.mozilla.com/firefox" target="_blank">{{ _('Download Firefox 4') }}</a></h3>
<p>{% blocktrans %}Enjoy the latest version featuring a new look, more tools and other goodness.{% endblocktrans %}</p>
</div>
</div>
</section>
<section id="language">
<a id="current-locale" href="#">{{ locale_code }}</a>
<ul id="language-selector">
{% for lang in LANGUAGES %}
{% ifequal lang.0 LANGUAGE_CODE %}
<li class="selected">{{ lang.1 }}</li>
{% else %}
<li><a href="{{ request.path|chlocale:lang.0 }}">{{ lang.1 }}</a></li>
{% endifequal %}
{% endfor %}
</ul>
</section>
<section id='legal'>
<p class="legal-blurb">{% blocktrans %}The Mozilla Firefox Mark Up campaign is a publicly accessible campaign site. If you choose to use your formal signature, then please recognize that it will be viewable to those with whom you share it online as well as to anyone who comes to this site.{% endblocktrans %}</p>
<p class="legal-blurb last">{% blocktrans %}Mozilla appeals to your sense of propriety and we ask that you avoid offensive or unlawful marks. Mozilla reserves the right to remove marks that are offensive and/or violate the law. See Mozillas Legal Notices for more information.{% endblocktrans %}</p>
<nav id="legal-links">
<ul>
<li><a href="http://www.mozilla.com/privacy-policy.html" target="_blank">{{ _('Privacy Policy') }}</a></li>
<li><a href="http://www.mozilla.com/about/legal.html" target="_blank">{{ _('Legal Notices') }}</a></li>
<li class="last"><a href="http://www.mozilla.com/legal/fraud-report/index.html" target="_blank">{{ _('Report Trademark Abuse') }}</a></li>
</ul>
</nav>
<p>{% blocktrans %}Except where otherwise noted, content on this site is licensed under the <a href="http://creativecommons.org/licenses/by-sa/3.0/us/" target="_blank">Creative Commons Attribution Share-Alike License v3.0</a> or any later version.{% endblocktrans %}</p>
</section>
</footer>
</body>
</html>

Просмотреть файл

@ -0,0 +1,18 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>FFDemo</title>
<style type="text/css">
body { width: 100%; margin: 100px auto; }
ul, li { clear: both; list-style: none; margin: 10px; }
label { display: block; float: left; width: 100px; }
</style>
</head>
<body>
{% block content %}
{% endblock %}
</body>
</html>

Просмотреть файл

@ -0,0 +1,13 @@
{% extends "about.html" %}
{% load i18n %}
{% block bodytag %}
<body class="behind-the-code about-code">
{% endblock %}
{% block about-content %}
<h1>{{ _('Code Repository') }}</h1>
{% blocktrans %}<p>We believe creativity should be shared. We believe everything on the Web should be accessible to the people who contribute to it. And that's why we chose to make the code behind Mark Up public.</p>
<p>We encourage you to use the code below to keep creating, collaborating and contributing.</p>
<p><a href="http://github.com/mozilla/markup">Get Code at GitHub</a></p>{% endblocktrans %}
{% endblock %}

Просмотреть файл

@ -0,0 +1,12 @@
{% extends "base.html" %}
{% load i18n %}
{% block bodytag %}
<body class="community">
{% endblock %}
{% block content %}
<section id="static-content">
<h1>{{ _('Community') }}</h1>
</section>
{% endblock %}

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше