--- a/.hgignore Wed Apr 10 18:38:21 2013 +0200
+++ b/.hgignore Fri Jun 07 11:55:46 2013 +0200
@@ -1,61 +1,32 @@
-syntax: regexp
-^script/stream/virtualenv/twitter_env$
-syntax: regexp
-^script/stream/virtualenv$
-syntax: regexp
-^script/rest/virtualenv$
-syntax: regexp
-^script/stream/streamwatcher\.py$
-syntax: regexp
-^script/virtualenv/res/twitter-1\.4\.2$
-syntax: regexp
-^script/stream/res$
-syntax: regexp
-^script/stream/.+\.db$
-.oauth_token
-
-syntax: regexp
-log.txt$
-
-syntax: regexp
-\.DS_Store$
-\.pyc
-
-syntax: regexp
-^script/virtualenv/distribute-0\.6\.14\.tar\.gz$
-syntax: regexp
-^script/virtualenv/venv.*$
-
-syntax: regexp
-^web/config\.php$
-^web/\.htaccess$
-
-syntax: regexp
-^web/CPV/config\.php$
-
-syntax: regexp
-^script/virtualenv/venv2$
-^script/virtualenv/venv_prod$
-
-syntax: regexp
-^\.settings$
syntax: glob
*.orig
syntax: regexp
+^script/stream/virtualenv/twitter_env$
+^script/stream/virtualenv$
+^script/rest/virtualenv$
+^script/stream/streamwatcher\.py$
+^script/virtualenv/res/twitter-1\.4\.2$
+^script/stream/res$
+^script/stream/.+\.db$
+.oauth_token
+log.txt$
+\.DS_Store$
+\.pyc
+^script/virtualenv/distribute-0\.6\.14\.tar\.gz$
+^script/virtualenv/venv.*$
+^web/config\.php$
+^web/\.htaccess$
+^web/CPV/config\.php$
+^script/virtualenv/venv2$
+^script/virtualenv/venv_prod$
+^\.settings$
^tweetcast/nodejs/node_modules$
-syntax: regexp
^tweetcast/server-gevent/server_setup\.py$
-syntax: regexp
^script/lib/iri_tweet/dist$
-syntax: regexp
^script/lib/iri_tweet/iri_tweet\.egg-info$
-syntax: regexp
^script/lib/tweetstream/dist$
-syntax: regexp
^script/lib/tweetstream/tweetstream\.egg-info$
-syntax: regexp
^script/virtualenv/script/env$
-syntax: regexp
-^script/virtualenv/script/project-boot\.py$
\ No newline at end of file
+^script/virtualenv/script/project-boot\.py$
--- a/.project Wed Apr 10 18:38:21 2013 +0200
+++ b/.project Fri Jun 07 11:55:46 2013 +0200
@@ -1,28 +1,34 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<projectDescription>
- <name>tweet_live</name>
- <comment></comment>
- <projects>
- </projects>
- <buildSpec>
- <buildCommand>
- <name>com.aptana.ide.core.unifiedBuilder</name>
- <arguments>
- </arguments>
- </buildCommand>
- </buildSpec>
- <natures>
- <nature>com.aptana.projects.webnature</nature>
- </natures>
- <filteredResources>
- <filter>
- <id>1312812919641</id>
- <name></name>
- <type>6</type>
- <matcher>
- <id>org.eclipse.ui.ide.multiFilter</id>
- <arguments>1.0-name-matches-false-false-.DS_Store</arguments>
- </matcher>
- </filter>
- </filteredResources>
-</projectDescription>
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>tweet_live</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.python.pydev.PyDevBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>com.aptana.ide.core.unifiedBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>com.aptana.projects.webnature</nature>
+ <nature>org.python.pydev.pythonNature</nature>
+ </natures>
+ <filteredResources>
+ <filter>
+ <id>1312812919641</id>
+ <name></name>
+ <type>6</type>
+ <matcher>
+ <id>org.eclipse.ui.ide.multiFilter</id>
+ <arguments>1.0-name-matches-false-false-.DS_Store</arguments>
+ </matcher>
+ </filter>
+ </filteredResources>
+</projectDescription>
--- a/.pydevproject Wed Apr 10 18:38:21 2013 +0200
+++ b/.pydevproject Fri Jun 07 11:55:46 2013 +0200
@@ -1,11 +1,11 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<?eclipse-pydev version="1.0"?>
-
-<pydev_project>
+<?eclipse-pydev version="1.0"?><pydev_project>
<pydev_property name="org.python.pydev.PYTHON_PROJECT_INTERPRETER">python_tl</pydev_property>
-<pydev_property name="org.python.pydev.PYTHON_PROJECT_VERSION">python 2.6</pydev_property>
+<pydev_property name="org.python.pydev.PYTHON_PROJECT_VERSION">python 2.7</pydev_property>
<pydev_pathproperty name="org.python.pydev.PROJECT_SOURCE_PATH">
-<path>/tweet_live/script/lib</path>
-<path>/tweet_live/script/lib/tweetstream</path>
+<path>/tweet_live/script/lib/iri_tweet</path>
+<path>/tweet_live/script/stream</path>
+<path>/tweet_live/script/rest</path>
+<path>/tweet_live/script/utils</path>
</pydev_pathproperty>
</pydev_project>
--- a/sbin/sync/sync_live Wed Apr 10 18:38:21 2013 +0200
+++ b/sbin/sync/sync_live Fri Jun 07 11:55:46 2013 +0200
@@ -23,4 +23,4 @@
rm -fr ~/tmp/tweet_live_V$1;
fi
-ssh iri@www.iri.centrepompidou.fr sudo apache2ctl restart
+#ssh iri@www.iri.centrepompidou.fr sudo apache2ctl restart
--- a/script/lib/iri_tweet/iri_tweet/__init__.py Wed Apr 10 18:38:21 2013 +0200
+++ b/script/lib/iri_tweet/iri_tweet/__init__.py Fri Jun 07 11:55:46 2013 +0200
@@ -21,3 +21,34 @@
__contact__ = "ymh.work@gmail.com"
__homepage__ = ""
__docformat__ = "restructuredtext"
+
+
+"""
+ .. data:: USER_AGENT
+
+ The default user agent string for stream objects
+"""
+
+USER_AGENT = "IRITweet %s" % __version__
+
+
+class IRITweetError(Exception):
+ """Base class for all IRITweet errors"""
+ pass
+
+
+class AuthenticationError(IRITweetError):
+ """Exception raised if the username/password is not accepted"""
+ pass
+
+
+class ConnectionError(IRITweetError):
+ """Raised when there are network problems. This means when there are
+ dns errors, network errors, twitter issues"""
+
+ def __init__(self, reason, details=None):
+ self.reason = reason
+ self.details = details
+
+ def __str__(self):
+ return '<ConnectionError %s>' % self.reason
--- a/script/lib/iri_tweet/iri_tweet/models.py Wed Apr 10 18:38:21 2013 +0200
+++ b/script/lib/iri_tweet/iri_tweet/models.py Fri Jun 07 11:55:46 2013 +0200
@@ -1,5 +1,6 @@
from sqlalchemy import (Boolean, Column, Enum, BigInteger, Integer, String,
- ForeignKey, DateTime, create_engine)
+ ForeignKey, DateTime, create_engine, event)
+from sqlalchemy.engine import Engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relationship, sessionmaker
import anyjson
@@ -11,12 +12,8 @@
Base = declarative_base()
APPLICATION_NAME = "IRI_TWITTER"
-CONSUMER_KEY = "54ThDZhpEjokcMgHJOMnQA"
-CONSUMER_SECRET = "wUoL9UL2T87tfc97R0Dff2EaqRzpJ5XGdmaN2XK3udA"
ACCESS_TOKEN_KEY = None
ACCESS_TOKEN_SECRET = None
-#ACCESS_TOKEN_KEY= "47312923-LiNTtz0I18YXMVIrFeTuhmH7bOvYsK6p3Ln2Dc"
-#ACCESS_TOKEN_SECRET = "r3LoXVcjImNAElUpWqTu2SG2xCdWFHkva7xeQoncA"
def adapt_date(date_str):
ts = email.utils.parsedate_tz(date_str) #@UndefinedVariable
@@ -32,7 +29,7 @@
def __init__(cls, name, bases, ns): #@NoSelf
def init(self, **kwargs):
- for key, value in kwargs.items():
+ for key, value in kwargs.iteritems():
if hasattr(self, key):
setattr(self, key, value)
super(cls, self).__init__()
@@ -82,6 +79,14 @@
'OK' : 1,
'ERROR' : 2,
'NOT_TWEET': 3,
+ 'DELETE': 4,
+ 'SCRUB_GEO': 5,
+ 'LIMIT': 6,
+ 'STATUS_WITHHELD': 7,
+ 'USER_WITHHELD': 8,
+ 'DISCONNECT': 9,
+ 'STALL_WARNING': 10,
+ 'DELETE_PENDING': 4
}
__metaclass__ = TweetMeta
@@ -90,6 +95,7 @@
ts = Column(DateTime, default=datetime.datetime.utcnow, index=True)
tweet_source_id = Column(Integer, ForeignKey('tweet_tweet_source.id'))
tweet_source = relationship("TweetSource", backref="logs")
+ status_id = Column(BigInteger, index=True, nullable=True, default=None)
status = Column(Integer)
error = Column(String)
error_stack = Column(String)
@@ -121,7 +127,7 @@
user = relationship("User", backref="tweets")
tweet_source_id = Column(Integer, ForeignKey('tweet_tweet_source.id'))
tweet_source = relationship("TweetSource", backref="tweet")
- entity_list = relationship(Entity, backref='tweet')
+ entity_list = relationship(Entity, backref='tweet', cascade="all, delete-orphan")
received_at = Column(DateTime, default=datetime.datetime.utcnow, index=True)
@@ -266,9 +272,17 @@
session_argname = [ 'autoflush','binds', "class_", "_enable_transaction_accounting","expire_on_commit", "extension", "query_cls", "twophase", "weak_identity_map", "autocommit"]
- kwargs_ce = dict((k, v) for k,v in kwargs.items() if (k not in session_argname and k != "create_all"))
+ kwargs_ce = dict((k, v) for k,v in kwargs.iteritems() if (k not in session_argname and k != "create_all"))
engine = create_engine(*args, **kwargs_ce)
+
+ if engine.name == "sqlite":
+ @event.listens_for(Engine, "connect")
+ def set_sqlite_pragma(dbapi_connection, connection_record):
+ cursor = dbapi_connection.cursor()
+ cursor.execute("PRAGMA foreign_keys=ON")
+ cursor.close()
+
metadata = Base.metadata
kwargs_sm = {'bind': engine}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/script/lib/iri_tweet/iri_tweet/processor.py Fri Jun 07 11:55:46 2013 +0200
@@ -0,0 +1,492 @@
+# -*- coding: utf-8 -*-
+'''
+Created on Apr 29, 2013
+
+@author: ymh
+'''
+from iri_tweet.models import (User, EntityType, adapt_json, MediaType, Media,
+ EntityMedia, Hashtag, EntityHashtag, EntityUser, EntityUrl, Url, Entity, Tweet,
+ TweetSource, TweetLog)
+from iri_tweet.utils import (ObjectsBuffer, adapt_fields, fields_adapter,
+ ObjectBufferProxy, get_oauth_token, clean_keys)
+from sqlalchemy.orm import joinedload
+import anyjson
+import logging
+import twitter
+import twitter_text
+
+
+class TwitterProcessorException(Exception):
+ pass
+
+class TwitterProcessor(object):
+ def __init__(self, json_dict, json_txt, source_id, session, consumer_token, access_token=None, token_filename=None, user_query_twitter=False, logger=None):
+
+ if json_dict is None and json_txt is None:
+ raise TwitterProcessorException("No json")
+
+ if json_dict is None:
+ self.json_dict = anyjson.deserialize(json_txt)
+ else:
+ self.json_dict = json_dict
+
+ if not json_txt:
+ self.json_txt = anyjson.serialize(json_dict)
+ else:
+ self.json_txt = json_txt
+
+ if "id" not in self.json_dict:
+ raise TwitterProcessorException("No id in json")
+
+ self.source_id = source_id
+ self.session = session
+ self.consumer_key = consumer_token[0]
+ self.consumer_secret = consumer_token[1]
+ self.token_filename = token_filename
+ self.access_token = access_token
+ self.obj_buffer = ObjectsBuffer()
+ self.user_query_twitter = user_query_twitter
+ if not logger:
+ self.logger = logging.getLogger(__name__)
+ else:
+ self.logger = logger
+
+ def process(self):
+ if self.source_id is None:
+ tweet_source = self.obj_buffer.add_object(TweetSource, None, {'original_json':self.json_txt}, True)
+ self.source_id = tweet_source.id
+ self.process_source()
+ self.obj_buffer.persists(self.session)
+
+ def process_source(self):
+ raise NotImplementedError()
+
+ def log_info(self):
+ return "Process tweet %s" % repr(self.__class__)
+
+
+class TwitterProcessorStatus(TwitterProcessor):
+
+ def __init__(self, json_dict, json_txt, source_id, session, consumer_token, access_token=None, token_filename=None, user_query_twitter=False, logger=None):
+ TwitterProcessor.__init__(self, json_dict, json_txt, source_id, session, consumer_token, access_token=access_token, token_filename=token_filename, user_query_twitter=user_query_twitter, logger=logger)
+
+ def __get_user(self, user_dict, do_merge):
+ self.logger.debug("Get user : " + repr(user_dict)) #@UndefinedVariable
+
+ user_dict = adapt_fields(user_dict, fields_adapter["stream"]["user"])
+
+ user_id = user_dict.get("id",None)
+ user_name = user_dict.get("screen_name", user_dict.get("name", None))
+
+ if user_id is None and user_name is None:
+ return None
+
+ user = None
+ if user_id:
+ user = self.obj_buffer.get(User, id=user_id)
+ else:
+ user = self.obj_buffer.get(User, screen_name=user_name)
+
+ #to do update user id needed
+ if user is not None:
+ user_created_at = None
+ if user.args is not None:
+ user_created_at = user.args.get('created_at', None)
+ if user_created_at is None and user_dict.get('created_at', None) is not None and do_merge:
+ if user.args is None:
+ user.args = user_dict
+ else:
+ user.args.update(user_dict)
+ return user
+
+ #todo : add methpds to objectbuffer to get buffer user
+ user_obj = None
+ if user_id:
+ user_obj = self.session.query(User).filter(User.id == user_id).first()
+ else:
+ user_obj = self.session.query(User).filter(User.screen_name.ilike(user_name)).first()
+
+ #todo update user if needed
+ if user_obj is not None:
+ if user_obj.created_at is not None or user_dict.get('created_at', None) is None or not do_merge :
+ user = ObjectBufferProxy(User, None, None, False, user_obj)
+ else:
+ user = self.obj_buffer.add_object(User, None, user_dict, True, user_obj)
+ return user
+
+ user_created_at = user_dict.get("created_at", None)
+
+ if user_created_at is None and self.user_query_twitter:
+
+ if self.access_token is not None:
+ acess_token_key, access_token_secret = self.access_token
+ else:
+ acess_token_key, access_token_secret = get_oauth_token(consumer_key=self.consumer_key, consumer_secret=self.consumer_secret, token_file_path=self.token_filename)
+ #TODO pass it as argument
+ t = twitter.Twitter(auth=twitter.OAuth(acess_token_key, access_token_secret, self.consumer_key, self.consumer_secret))
+ try:
+ if user_id:
+ user_dict = t.users.show(user_id=user_id)
+ else:
+ user_dict = t.users.show(screen_name=user_name)
+ except Exception as e:
+ self.logger.info("get_user : TWITTER ERROR : " + repr(e)) #@UndefinedVariable
+ self.logger.info("get_user : TWITTER ERROR : " + str(e)) #@UndefinedVariable
+ return None
+
+ if "id" not in user_dict:
+ return None
+
+ #TODO filter get, wrap in proxy
+ user_obj = self.session.query(User).filter(User.id == user_dict["id"]).first()
+
+ if user_obj is not None and not do_merge:
+ return ObjectBufferProxy(User, None, None, False, user_obj)
+ else:
+ return self.obj_buffer.add_object(User, None, user_dict, True)
+
+ def __get_or_create_object(self, klass, filter_by_kwargs, filter_arg, creation_kwargs, must_flush, do_merge):
+
+ obj_proxy = self.obj_buffer.get(klass, **filter_by_kwargs)
+ if obj_proxy is None:
+ query = self.session.query(klass)
+ if filter_arg is not None:
+ query = query.filter(filter_arg)
+ else:
+ query = query.filter_by(**filter_by_kwargs)
+ obj_instance = query.first()
+ if obj_instance is not None:
+ if not do_merge:
+ obj_proxy = ObjectBufferProxy(klass, None, None, False, obj_instance)
+ else:
+ obj_proxy = self.obj_buffer.add_object(klass, None, creation_kwargs, must_flush, obj_instance)
+ if obj_proxy is None:
+ obj_proxy = self.obj_buffer.add_object(klass, None, creation_kwargs, must_flush)
+ return obj_proxy
+
+
+ def __process_entity(self, ind, ind_type):
+ self.logger.debug("Process_entity : " + repr(ind) + " : " + repr(ind_type)) #@UndefinedVariable
+
+ ind = clean_keys(ind)
+
+ entity_type = self.__get_or_create_object(EntityType, {'label':ind_type}, None, {'label':ind_type}, True, False)
+
+ entity_dict = {
+ "indice_start" : ind["indices"][0],
+ "indice_end" : ind["indices"][1],
+ "tweet_id" : self.tweet.id,
+ "entity_type_id" : entity_type.id,
+ "source" : adapt_json(ind)
+ }
+
+ def process_medias():
+
+ media_id = ind.get('id', None)
+ if media_id is None:
+ return None, None
+
+ type_str = ind.get("type", "photo")
+ media_type = self.__get_or_create_object(MediaType, {'label': type_str}, None, {'label':type_str}, True, False)
+ media_ind = adapt_fields(ind, fields_adapter["entities"]["medias"])
+ if "type" in media_ind:
+ del(media_ind["type"])
+ media_ind['type_id'] = media_type.id
+ media = self.__get_or_create_object(Media, {'id':media_id}, None, media_ind, True, False)
+
+ entity_dict['media_id'] = media.id
+ return EntityMedia, entity_dict
+
+ def process_hashtags():
+ text = ind.get("text", ind.get("hashtag", None))
+ if text is None:
+ return None, None
+ ind['text'] = text
+ hashtag = self.__get_or_create_object(Hashtag, {'text':text}, Hashtag.text.ilike(text), ind, True, False)
+ entity_dict['hashtag_id'] = hashtag.id
+ return EntityHashtag, entity_dict
+
+ def process_user_mentions():
+ user_mention = self.__get_user(ind, False)
+ if user_mention is None:
+ entity_dict['user_id'] = None
+ else:
+ entity_dict['user_id'] = user_mention.id
+ return EntityUser, entity_dict
+
+ def process_urls():
+ url = self.__get_or_create_object(Url, {'url':ind["url"]}, None, ind, True, False)
+ entity_dict['url_id'] = url.id
+ return EntityUrl, entity_dict
+
+ #{'': lambda }
+ entity_klass, entity_dict = {
+ 'hashtags': process_hashtags,
+ 'user_mentions' : process_user_mentions,
+ 'urls' : process_urls,
+ 'media': process_medias,
+ }.get(ind_type, lambda: (Entity, entity_dict))()
+
+ self.logger.debug("Process_entity entity_dict: " + repr(entity_dict)) #@UndefinedVariable
+ if entity_klass:
+ self.obj_buffer.add_object(entity_klass, None, entity_dict, False)
+
+
+ def __process_twitter(self):
+
+ tweet_nb = self.session.query(Tweet).filter(Tweet.id == self.json_dict["id"]).count()
+ if tweet_nb > 0:
+ return
+
+ ts_copy = adapt_fields(self.json_dict, fields_adapter["stream"]["tweet"])
+
+ # get or create user
+ user = self.__get_user(self.json_dict["user"], True)
+ if user is None:
+ self.logger.warning("USER not found " + repr(self.json_dict["user"])) #@UndefinedVariable
+ ts_copy["user_id"] = None
+ else:
+ ts_copy["user_id"] = user.id
+
+ del(ts_copy['user'])
+ ts_copy["tweet_source_id"] = self.source_id
+
+ self.tweet = self.obj_buffer.add_object(Tweet, None, ts_copy, True)
+
+ self.__process_entities()
+
+
+ def __process_entities(self):
+ if "entities" in self.json_dict:
+ for ind_type, entity_list in self.json_dict["entities"].iteritems():
+ for ind in entity_list:
+ self.__process_entity(ind, ind_type)
+ else:
+
+ text = self.tweet.text
+ extractor = twitter_text.Extractor(text)
+ for ind in extractor.extract_hashtags_with_indices():
+ self.__process_entity(ind, "hashtags")
+
+ for ind in extractor.extract_urls_with_indices():
+ self.__process_entity(ind, "urls")
+
+ for ind in extractor.extract_mentioned_screen_names_with_indices():
+ self.__process_entity(ind, "user_mentions")
+
+
+
+ def process_source(self):
+
+ status_id = self.json_dict["id"]
+ log = self.session.query(TweetLog).filter(TweetLog.status_id==status_id).first()
+ if(log):
+ self.obj_buffer.add_object(TweetLog, log, {'status': TweetLog.TWEET_STATUS['DELETE'], 'status_id': None})
+ self.session.query(TweetSource).filter(TweetSource.id==self.source_id).delete()
+ else:
+ self.__process_twitter()
+
+ self.obj_buffer.add_object(TweetLog, None, {'tweet_source_id':self.source_id, 'status':TweetLog.TWEET_STATUS['OK']}, True)
+
+ def log_info(self):
+ screen_name = self.json_dict.get("user",{}).get("screen_name","")
+ return u"Process Tweet from %s : %s" % (screen_name, self.json_dict.get('text',u""))
+
+
+
+class TwitterProcessorDelete(TwitterProcessor):
+ """
+ {
+ "delete":{
+ "status":{
+ "id":1234,
+ "id_str":"1234",
+ "user_id":3,
+ "user_id_str":"3"
+ }
+ }
+ }
+ """
+ def __init__(self, json_dict, json_txt, source_id, session, consumer_token, access_token=None, token_filename=None, user_query_twitter=False, logger=None):
+ TwitterProcessor.__init__(self, json_dict, json_txt, source_id, session, consumer_token, access_token=access_token, token_filename=token_filename, user_query_twitter=user_query_twitter, logger=logger)
+
+ def process(self):
+
+ #find tweet
+ tweet_id = self.json_dict.get('delete',{}).get('status',{}).get('id',None)
+ if tweet_id:
+ t = self.session.query(Tweet).options(joinedload(Tweet.tweet_source)).filter(Tweet.id == tweet_id).first()
+ if t:
+ tsource = t.tweet_source
+ self.session.delete(t)
+ self.session.query(TweetLog).filter(TweetLog.tweet_source_id == tsource.id).delete()
+ self.session.delete(tsource)
+ self.obj_buffer.add_object(TweetLog, None, {'tweet_source_id':self.source_id, 'status':TweetLog.TWEET_STATUS['DELETE']}, True)
+ else:
+ self.obj_buffer.add_object(TweetLog, None, {'tweet_source_id':self.source_id, 'status_id': tweet_id,'status':TweetLog.TWEET_STATUS['DELETE_PENDING']}, True)
+
+ def log_info(self):
+ status_del = self.json_dict.get('delete', {}).get("status",{})
+ return u"Process delete for %s : %s" % (status_del.get('user_id_str',u""), status_del.get('id_str',u""))
+
+class TwitterProcessorScrubGeo(TwitterProcessor):
+ """
+ {
+ "scrub_geo":{
+ "user_id":14090452,
+ "user_id_str":"14090452",
+ "up_to_status_id":23260136625,
+ "up_to_status_id_str":"23260136625"
+ }
+ }
+ """
+
+ def __init__(self, json_dict, json_txt, source_id, session, consumer_token, access_token=None, token_filename=None, user_query_twitter=False, logger=None):
+ TwitterProcessor.__init__(self, json_dict, json_txt, source_id, session, consumer_token, access_token=access_token, token_filename=token_filename, user_query_twitter=user_query_twitter, logger=logger)
+
+ def process_source(self):
+ up_to_status_id = self.json_dict.get("scrub_geo", {}).get("up_to_status_id", None)
+ if not up_to_status_id:
+ return
+ tweets = self.session.query(Tweet).options(joinedload(Tweet.tweet_source)).filter(Tweet.id <= up_to_status_id)
+ for t in tweets:
+ self.obj_buffer.add_object(Tweet, t, {'geo': None})
+ tsource = t.tweet_source
+ tsource_dict = anyjson.serialize(tsource.original_json)
+ if tsource_dict.get("geo", None):
+ tsource_dict["geo"] = None
+ self.obj_buffer.add_object(TweetSource, tsource, {'original_json': anyjson.serialize(tsource_dict)})
+ self.obj_buffer.add_object(TweetLog, None, {'tweet_source_id':self.source_id, 'status':TweetLog.TWEET_STATUS['SCRUB_GEO']}, True)
+
+ def log_info(self):
+ return u"Process scrub geo for %s : %s" % (self.json_dict["scrub_geo"].get('user_id_str',u""), self.json_dict["scrub_geo"].get('id_str',u""))
+
+
+class TwitterProcessorLimit(TwitterProcessor):
+ """
+ {
+ "limit":{
+ "track":1234
+ }
+ }
+ """
+ def __init__(self, json_dict, json_txt, source_id, session, consumer_token, access_token=None, token_filename=None, user_query_twitter=False, logger=None):
+ TwitterProcessor.__init__(self, json_dict, json_txt, source_id, session, consumer_token, access_token=access_token, token_filename=token_filename, user_query_twitter=user_query_twitter, logger=logger)
+
+ def process_source(self):
+ """
+ do nothing, just log the information
+ """
+ self.obj_buffer.add_object(TweetLog, None, {'tweet_source_id':self.source_id, 'status':TweetLog.TWEET_STATUS['LIMIT'], 'error':self.json_txt}, True)
+
+ def log_info(self):
+ return u"Process limit %d " % self.json_dict.get("limit", {}).get('track', 0)
+
+class TwitterProcessorStatusWithheld(TwitterProcessor):
+ """
+ {
+ "status_withheld":{
+ "id":1234567890,
+ "user_id":123456,
+ "withheld_in_countries":["DE", "AR"]
+ }
+ }
+ """
+ def __init__(self, json_dict, json_txt, source_id, session, consumer_token, access_token=None, token_filename=None, user_query_twitter=False, logger=None):
+ TwitterProcessor.__init__(self, json_dict, json_txt, source_id, session, consumer_token, access_token=access_token, token_filename=token_filename, user_query_twitter=user_query_twitter, logger=logger)
+
+ def process_source(self):
+ """
+ do nothing, just log the information
+ """
+ self.obj_buffer.add_object(TweetLog, None, {'tweet_source_id':self.source_id, 'status':TweetLog.TWEET_STATUS['STATUS_WITHHELD'], 'error':self.json_txt}, True)
+
+ def log_info(self):
+ status_withheld = self.json_dict.get("status_withheld",{})
+ return u"Process status withheld status id %d from user %d in countries %s" %(status_withheld.get("id",0), status_withheld.get("user_id",0), u",".join(status_withheld.get("withheld_in_countries",[])))
+
+class TwitterProcessorUserWithheld(TwitterProcessor):
+ """
+ {
+ "user_withheld":{
+ "id":123456,
+ "withheld_in_countries":["DE","AR"]
+ }
+ }
+ """
+ def __init__(self, json_dict, json_txt, source_id, session, consumer_token, access_token=None, token_filename=None, user_query_twitter=False, logger=None):
+ TwitterProcessor.__init__(self, json_dict, json_txt, source_id, session, consumer_token, access_token=access_token, token_filename=token_filename, user_query_twitter=user_query_twitter, logger=logger)
+
+ def process_source(self):
+ """
+ do nothing, just log the information
+ """
+ self.obj_buffer.add_object(TweetLog, None, {'tweet_source_id':self.source_id, 'status':TweetLog.TWEET_STATUS['USER_WITHHELD'], 'error':self.json_txt}, True)
+
+
+ def log_info(self):
+ user_withheld = self.json_dict.get("user_withheld", {})
+ return u"Process user withheld %d in countries %s" % (user_withheld.get("id",0), u"".join(user_withheld.get("withheld_in_countries",[])))
+
+class TwitterProcessorDisconnect(TwitterProcessor):
+ """
+ {
+ "disconnect":{
+ "code": 4,
+ "stream_name":"< A stream identifier >",
+ "reason":"< Human readable status message >"
+ }
+ }
+ """
+ def __init__(self, json_dict, json_txt, source_id, session, consumer_token, access_token=None, token_filename=None, user_query_twitter=False, logger=None):
+ TwitterProcessor.__init__(self, json_dict, json_txt, source_id, session, consumer_token, access_token=access_token, token_filename=token_filename, user_query_twitter=user_query_twitter, logger=logger)
+
+ def process_source(self):
+ """
+ do nothing, just log the information
+ """
+ self.obj_buffer.add_object(TweetLog, None, {'tweet_source_id':self.source_id, 'status':TweetLog.TWEET_STATUS['DISCONNECT'], 'error':self.json_txt}, True)
+
+ def log_info(self):
+ disconnect = self.json_dict.get("disconnect",{})
+ return u"Process disconnect stream %s code %d reason %s" % (disconnect.get("stream_name",""), disconnect.get("code",0), disconnect.get("reason",""))
+
+class TwitterProcessorStallWarning(TwitterProcessor):
+ """
+ {
+ "warning":{
+ "code":"FALLING_BEHIND",
+ "message":"Your connection is falling behind and messages are being queued for delivery to you. Your queue is now over 60% full. You will be disconnected when the queue is full.",
+ "percent_full": 60
+ }
+ }
+ """
+ def __init__(self, json_dict, json_txt, source_id, session, consumer_token, access_token=None, token_filename=None, user_query_twitter=False, logger=None):
+ TwitterProcessor.__init__(self, json_dict, json_txt, source_id, session, consumer_token, access_token=access_token, token_filename=token_filename, user_query_twitter=user_query_twitter, logger=logger)
+
+ def process_source(self):
+ """
+ do nothing, just log the information
+ """
+ self.obj_buffer.add_object(TweetLog, None, {'tweet_source_id':self.source_id, 'status':TweetLog.TWEET_STATUS['STALL_WARNING'], 'error':self.json_txt}, True)
+
+ def log_info(self):
+ warning = self.json_dict.get("warning",{})
+ return u"Process stall warning %d%% code %s, message %s" % (warning.get("percent_full",0),warning.get("code",u""), warning.get("message", u""))
+
+TWEET_PROCESSOR_MAP = {
+ 'text': TwitterProcessorStatus,
+ 'delete': TwitterProcessorDelete,
+ 'scrub_geo': TwitterProcessorScrubGeo,
+ 'limit': TwitterProcessorLimit,
+ 'status_withheld': TwitterProcessorStatusWithheld,
+ 'user_withheld': TwitterProcessorUserWithheld,
+ 'disconnect': TwitterProcessorDisconnect,
+ 'warning': TwitterProcessorStallWarning
+}
+
+def get_processor(tweet_dict):
+ for processor_key,processor_klass in TWEET_PROCESSOR_MAP.iteritems():
+ if processor_key in tweet_dict:
+ return processor_klass
+ return None
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/script/lib/iri_tweet/iri_tweet/stream.py Fri Jun 07 11:55:46 2013 +0200
@@ -0,0 +1,399 @@
+# -*- coding: utf-8 -*-
+'''
+Created on Mar 22, 2012
+
+@author: ymh
+
+Module directly inspired by tweetstream
+
+'''
+import time
+import requests
+from requests.utils import stream_decode_response_unicode
+import anyjson
+import select
+
+from . import USER_AGENT, ConnectionError, AuthenticationError
+
+
+def iter_content_non_blocking(req, max_chunk_size=4096, decode_unicode=False, timeout=-1):
+
+ if req._content_consumed:
+ raise RuntimeError(
+ 'The content for this response was already consumed'
+ )
+
+ req.raw._fp.fp._sock.setblocking(False)
+
+ def generate():
+ chunk_size = 1
+ while True:
+ if timeout < 0:
+ rlist,_,_ = select.select([req.raw._fp.fp._sock], [], [])
+ else:
+ rlist,_,_ = select.select([req.raw._fp.fp._sock], [], [], timeout)
+
+ if not rlist:
+ continue
+
+ try:
+ chunk = req.raw.read(chunk_size, decode_content=True)
+ if not chunk:
+ break
+ if len(chunk) >= chunk_size and chunk_size < max_chunk_size:
+ chunk_size = min(chunk_size*2, max_chunk_size)
+ elif len(chunk) < chunk_size/2 and chunk_size < max_chunk_size:
+ chunk_size = max(chunk_size/2,1)
+ yield chunk
+ except requests.exceptions.SSLError as e:
+ if e.errno == 2:
+ # Apparently this means there was nothing in the socket buf
+ pass
+ else:
+ raise
+
+ req._content_consumed = True
+
+ gen = generate()
+
+ if decode_unicode:
+ gen = stream_decode_response_unicode(gen, req)
+
+ return gen
+
+
+
+
+class BaseStream(object):
+
+ """A network connection to Twitters streaming API
+
+ :param auth: requests auth object.
+ :keyword catchup: Number of tweets from the past to get before switching to
+ live stream.
+ :keyword raw: If True, return each tweet's raw data direct from the socket,
+ without UTF8 decoding or parsing, rather than a parsed object. The
+ default is False.
+ :keyword timeout: If non-None, set a timeout in seconds on the receiving
+ socket. Certain types of network problems (e.g., disconnecting a VPN)
+ can cause the connection to hang, leading to indefinite blocking that
+ requires kill -9 to resolve. Setting a timeout leads to an orderly
+ shutdown in these cases. The default is None (i.e., no timeout).
+ :keyword url: Endpoint URL for the object. Note: you should not
+ need to edit this. It's present to make testing easier.
+
+ .. attribute:: connected
+
+ True if the object is currently connected to the stream.
+
+ .. attribute:: url
+
+ The URL to which the object is connected
+
+ .. attribute:: starttime
+
+ The timestamp, in seconds since the epoch, the object connected to the
+ streaming api.
+
+ .. attribute:: count
+
+ The number of tweets that have been returned by the object.
+
+ .. attribute:: rate
+
+ The rate at which tweets have been returned from the object as a
+ float. see also :attr: `rate_period`.
+
+ .. attribute:: rate_period
+
+ The ammount of time to sample tweets to calculate tweet rate. By
+ default 10 seconds. Changes to this attribute will not be reflected
+ until the next time the rate is calculated. The rate of tweets vary
+ with time of day etc. so it's usefull to set this to something
+ sensible.
+
+ .. attribute:: user_agent
+
+ User agent string that will be included in the request. NOTE: This can
+ not be changed after the connection has been made. This property must
+ thus be set before accessing the iterator. The default is set in
+ :attr: `USER_AGENT`.
+ """
+
+ def __init__(self, auth,
+ raw=False, timeout=-1, url=None, compressed=False, chunk_size=4096, logger=None):
+ self._conn = None
+ self._rate_ts = None
+ self._rate_cnt = 0
+ self._auth = auth
+ self.raw_mode = raw
+ self.timeout = timeout
+ self._compressed = compressed
+
+ self.rate_period = 10 # in seconds
+ self.connected = False
+ self.starttime = None
+ self.count = 0
+ self.rate = 0
+ self.user_agent = USER_AGENT
+ self.chunk_size = chunk_size
+ if url: self.url = url
+
+ self.muststop = False
+ self._logger = logger
+
+ self._iter = self.__iter__()
+
+
+ def __enter__(self):
+ return self
+
+ def __exit__(self, *params):
+ self.close()
+ return False
+
+ def _init_conn(self):
+ """Open the connection to the twitter server"""
+
+ if self._logger : self._logger.debug("BaseStream Open the connection to the twitter server")
+
+ headers = {'User-Agent': self.user_agent}
+
+ if self._compressed:
+ headers['Accept-Encoding'] = "deflate, gzip"
+
+ postdata = self._get_post_data() or {}
+ postdata['stall_warnings'] = 'true'
+
+ if self._logger : self._logger.debug("BaseStream init connection url " + repr(self.url))
+ if self._logger : self._logger.debug("BaseStream init connection headers " + repr(headers))
+ if self._logger : self._logger.debug("BaseStream init connection data " + repr(postdata))
+
+ self._resp = requests.post(self.url, auth=self._auth, headers=headers, data=postdata, stream=True)
+ if self._logger : self._logger.debug("BaseStream init connection " + repr(self._resp))
+
+ self._resp.raise_for_status()
+ self.connected = True
+
+ if not self._rate_ts:
+ self._rate_ts = time.time()
+ if not self.starttime:
+ self.starttime = time.time()
+
+
+ def _get_post_data(self):
+ """Subclasses that need to add post data to the request can override
+ this method and return post data. The data should be in the format
+ returned by urllib.urlencode."""
+ return None
+
+ def testmuststop(self):
+ if callable(self.muststop):
+ return self.muststop()
+ else:
+ return self.muststop
+
+ def _update_rate(self):
+ rate_time = time.time() - self._rate_ts
+ if not self._rate_ts or rate_time > self.rate_period:
+ self.rate = self._rate_cnt / rate_time
+ self._rate_cnt = 0
+ self._rate_ts = time.time()
+
+ def _iter_object(self):
+
+# for line in self._resp.iter_lines():
+# yield line
+# pending = None
+#
+# for chunk in self._resp.iter_content(chunk_size=self.chunk_size, decode_unicode=None):
+#
+# if pending is not None:
+# chunk = pending + chunk
+# lines = chunk.splitlines()
+#
+# if lines and lines[-1] and chunk and lines[-1][-1] == chunk[-1]:
+# pending = lines.pop()
+# else:
+# pending = None
+#
+# for line in lines:
+# yield line
+#
+# if pending is not None:
+# yield pending
+
+ pending = None
+ has_stopped = False
+
+ if self._logger : self._logger.debug("BaseStream _iter_object")
+
+ for chunk in self._resp.iter_content(
+ chunk_size=self.chunk_size,
+ decode_unicode=None):
+
+ if self._logger : self._logger.debug("BaseStream _iter_object loop")
+ if self.testmuststop():
+ has_stopped = True
+ break
+
+ if pending is not None:
+ chunk = pending + chunk
+ lines = chunk.split('\r')
+
+ if lines and lines[-1] and chunk and lines[-1][-1] == chunk[-1]:
+ pending = lines.pop()
+ else:
+ pending = None
+
+ for line in lines:
+ yield line.strip('\n')
+
+ if self.testmuststop():
+ has_stopped = True
+ break
+
+ if pending is not None:
+ yield pending
+ if has_stopped:
+ raise StopIteration()
+
+ def __iter__(self):
+
+ if self._logger : self._logger.debug("BaseStream __iter__")
+ if not self.connected:
+ if self._logger : self._logger.debug("BaseStream __iter__ not connected, connecting")
+ self._init_conn()
+
+ if self._logger : self._logger.debug("BaseStream __iter__ connected")
+
+ for line in self._iter_object():
+
+ if self._logger : self._logger.debug("BaseStream __iter__ line %s " % repr(line))
+
+ if not line:
+ continue
+
+ if (self.raw_mode):
+ tweet = line
+ else:
+ line = line.decode("utf8")
+ try:
+ tweet = anyjson.deserialize(line)
+ except ValueError:
+ self.close()
+ raise ConnectionError("Got invalid data from twitter", details=line)
+ if 'text' in tweet:
+ self.count += 1
+ self._rate_cnt += 1
+ self._update_rate()
+ yield tweet
+
+
+ def next(self):
+ """Return the next available tweet. This call is blocking!"""
+ return self._iter.next()
+
+
+ def close(self):
+ """
+ Close the connection to the streaming server.
+ """
+ self.connected = False
+
+
+class FilterStream(BaseStream):
+ url = "https://stream.twitter.com/1.1/statuses/filter.json"
+
+ def __init__(self, auth, follow=None, locations=None,
+ track=None, url=None, raw=False, timeout=None, compressed=False, chunk_size=requests.models.ITER_CHUNK_SIZE, logger=None):
+ self._follow = follow
+ self._locations = locations
+ self._track = track
+ # remove follow, locations, track
+ BaseStream.__init__(self, auth, url=url, raw=raw, timeout=timeout, compressed=compressed, chunk_size=chunk_size, logger=logger)
+
+ def _get_post_data(self):
+ postdata = {}
+ if self._follow: postdata["follow"] = ",".join([str(e) for e in self._follow])
+ if self._locations: postdata["locations"] = ",".join(self._locations)
+ if self._track: postdata["track"] = ",".join(self._track)
+ return postdata
+
+
+class SafeStreamWrapper(object):
+
+ def __init__(self, base_stream, logger=None, error_cb=None, max_reconnects=-1, initial_tcp_wait=250, initial_http_wait=5000, max_wait=240000):
+ self._stream = base_stream
+ self._logger = logger
+ self._error_cb = error_cb
+ self._max_reconnects = max_reconnects
+ self._initial_tcp_wait = initial_tcp_wait
+ self._initial_http_wait = initial_http_wait
+ self._max_wait = max_wait
+ self._retry_wait = 0
+ self._retry_nb = 0
+ self._reconnects = 0
+
+ def __post_process_error(self,e):
+ # Note: error_cb is not called on the last error since we
+ # raise a ConnectionError instead
+ if callable(self._error_cb):
+ self._error_cb(e)
+ if self._logger: self._logger.info("stream sleeping for %d ms " % self._retry_wait)
+ time.sleep(float(self._retry_wait)/1000.0)
+
+
+ def __process_tcp_error(self,e):
+ if self._logger: self._logger.debug("connection error type :" + repr(type(e)))
+ if self._logger: self._logger.debug("connection error :" + repr(e))
+
+ self._reconnects += 1
+ if self._max_reconnects >= 0 and self._reconnects > self._max_reconnects:
+ raise ConnectionError("Too many retries")
+ if self._retry_wait < self._max_wait:
+ self._retry_wait += self._initial_tcp_wait
+ if self._retry_wait > self._max_wait:
+ self._retry_wait = self._max_wait
+
+ self.__post_process_error(e)
+
+
+ def __process_http_error(self,e):
+ if self._logger: self._logger.debug("http error type %s" % (repr(type(e))))
+ if self._logger: self._logger.debug("http error on %s : %s" % (e.response.url,e.message))
+
+ if self._retry_wait < self._max_wait:
+ self._retry_wait = 2*self._retry_wait if self._retry_wait > 0 else self._initial_http_wait
+ if self._retry_wait > self._max_wait:
+ self._retry_wait = self._max_wait
+
+ self.__post_process_error(e)
+
+ def __iter__(self):
+ while not self._stream.testmuststop():
+ self._retry_nb += 1
+ try:
+ if self._logger: self._logger.debug("inner loop")
+ for tweet in self._stream:
+ if self._logger: self._logger.debug("tweet : " + repr(tweet))
+ self._reconnects = 0
+ self._retry_wait = 0
+ if not tweet.strip():
+ if self._logger: self._logger.debug("Empty Tweet received : PING")
+ continue
+ yield tweet
+ except requests.exceptions.HTTPError as e:
+ if e.response.status_code == 401:
+ if self._logger: self._logger.debug("SafeStreamWrapper Connection Error http error on %s : %s" % (e.response.url,e.message))
+ raise AuthenticationError("Error connecting to %s : %s : %s - %s" % (e.response.url,e.message, repr(e.response.headers),repr(e.response.text)))
+ if e.response.status_code > 200:
+ self.__process_http_error(e)
+ else:
+ self.__process_tcp_error(e)
+ except (ConnectionError, requests.exceptions.ConnectionError, requests.exceptions.Timeout, requests.exceptions.RequestException) as e:
+ self.__process_tcp_error(e)
+
+
+
+
\ No newline at end of file
--- a/script/lib/iri_tweet/iri_tweet/tests.py Wed Apr 10 18:38:21 2013 +0200
+++ b/script/lib/iri_tweet/iri_tweet/tests.py Fri Jun 07 11:55:46 2013 +0200
@@ -3,10 +3,14 @@
from sqlalchemy.orm import relationship, backref
import unittest #@UnresolvedImport
from sqlalchemy.orm import sessionmaker
-from iri_tweet.utils import ObjectsBuffer, TwitterProcessor
+from iri_tweet.utils import ObjectsBuffer
+from iri_tweet.processor import TwitterProcessorStatus
from iri_tweet import models
import tempfile #@UnresolvedImport
import os
+import logging
+
+logger = logging.getLogger(__name__);
Base = declarative_base()
@@ -129,12 +133,12 @@
def setUp(self):
self.engine, self.metadata, sessionMaker = models.setup_database('sqlite:///:memory:', echo=True)
self.session = sessionMaker()
- file, self.tmpfilepath = tempfile.mkstemp()
- os.close(file)
+ tmpfile, self.tmpfilepath = tempfile.mkstemp()
+ os.close(tmpfile)
def testTwitterProcessor(self):
- tp = TwitterProcessor(None, original_json, None, self.session, self.tmpfilepath)
+ tp = TwitterProcessorStatus(None, original_json, None, self.session, self.tmpfilepath, logger)
tp.process()
self.session.commit()
@@ -154,7 +158,7 @@
def testTwitterProcessorMedia(self):
- tp = TwitterProcessor(None, original_json_media, None, self.session, self.tmpfilepath)
+ tp = TwitterProcessorStatus(None, original_json_media, None, self.session, self.tmpfilepath, logger)
tp.process()
self.session.commit()
@@ -174,7 +178,7 @@
def testTwitterProcessorMediaOthers(self):
- tp = TwitterProcessor(None, original_json_media_others, None, self.session, self.tmpfilepath)
+ tp = TwitterProcessorStatus(None, original_json_media_others, None, self.session, self.tmpfilepath, logger)
tp.process()
self.session.commit()
--- a/script/lib/iri_tweet/iri_tweet/utils.py Wed Apr 10 18:38:21 2013 +0200
+++ b/script/lib/iri_tweet/iri_tweet/utils.py Fri Jun 07 11:55:46 2013 +0200
@@ -1,25 +1,22 @@
-from models import (Tweet, User, Hashtag, EntityHashtag, EntityUser, Url,
- EntityUrl, CONSUMER_KEY, CONSUMER_SECRET, APPLICATION_NAME, ACCESS_TOKEN_KEY,
- ACCESS_TOKEN_SECRET, adapt_date, adapt_json, TweetSource, TweetLog, MediaType,
- Media, EntityMedia, Entity, EntityType)
-from sqlalchemy.sql import select, or_ #@UnresolvedImport
-import Queue #@UnresolvedImport
-import anyjson #@UnresolvedImport
+from models import (Tweet, User, Hashtag, EntityHashtag, APPLICATION_NAME, ACCESS_TOKEN_SECRET, adapt_date, adapt_json,
+ ACCESS_TOKEN_KEY)
+from sqlalchemy.sql import select, or_
+import Queue
import codecs
import datetime
import email.utils
import logging
import math
import os.path
+import socket
import sys
-import twitter.oauth #@UnresolvedImport
-import twitter.oauth_dance #@UnresolvedImport
-import twitter_text #@UnresolvedImport
+import twitter.oauth
+import twitter.oauth_dance
CACHE_ACCESS_TOKEN = {}
-def get_oauth_token(token_file_path=None, check_access_token=True, application_name=APPLICATION_NAME, consumer_key=CONSUMER_KEY, consumer_secret=CONSUMER_SECRET):
+def get_oauth_token(consumer_key, consumer_secret, token_file_path=None, check_access_token=True, application_name=APPLICATION_NAME):
global CACHE_ACCESS_TOKEN
@@ -34,24 +31,27 @@
if res is not None and check_access_token:
get_logger().debug("get_oauth_token : Check oauth tokens") #@UndefinedVariable
- t = twitter.Twitter(auth=twitter.OAuth(res[0], res[1], CONSUMER_KEY, CONSUMER_SECRET))
+ t = twitter.Twitter(auth=twitter.OAuth(res[0], res[1], consumer_key, consumer_secret))
status = None
try:
- status = t.account.rate_limit_status()
+ status = t.application.rate_limit_status(resources="account")
except Exception as e:
- get_logger().debug("get_oauth_token : error getting rate limit status %s" % repr(e))
+ get_logger().debug("get_oauth_token : error getting rate limit status %s " % repr(e))
+ get_logger().debug("get_oauth_token : error getting rate limit status %s " % str(e))
status = None
get_logger().debug("get_oauth_token : Check oauth tokens : status %s" % repr(status)) #@UndefinedVariable
- if status is None or status['remaining_hits'] == 0:
+ if status is None or status.get("resources",{}).get("account",{}).get('/account/verify_credentials',{}).get('remaining',0) == 0:
get_logger().debug("get_oauth_token : Problem with status %s" % repr(status))
res = None
if res is None:
get_logger().debug("get_oauth_token : doing the oauth dance")
res = twitter.oauth_dance(application_name, consumer_key, consumer_secret, token_file_path)
+
CACHE_ACCESS_TOKEN[application_name] = res
+ get_logger().debug("get_oauth_token : done got %s" % repr(res))
return res
def parse_date(date_str):
@@ -59,7 +59,7 @@
return datetime.datetime(*ts[0:7])
def clean_keys(dict_val):
- return dict([(str(key),value) for key,value in dict_val.items()])
+ return dict([(str(key),value) for key,value in dict_val.iteritems()])
fields_adapter = {
'stream': {
@@ -100,7 +100,7 @@
return adapter_mapping[field](value)
else:
return value
- return dict([(str(k),adapt_one_field(k,v)) for k,v in fields_dict.items()])
+ return dict([(str(k),adapt_one_field(k,v)) for k,v in fields_dict.iteritems()])
class ObjectBufferProxy(object):
@@ -113,7 +113,7 @@
def persists(self, session):
new_args = [arg() if callable(arg) else arg for arg in self.args] if self.args is not None else []
- new_kwargs = dict([(k,v()) if callable(v) else (k,v) for k,v in self.kwargs.items()]) if self.kwargs is not None else {}
+ new_kwargs = dict([(k,v()) if callable(v) else (k,v) for k,v in self.kwargs.iteritems()]) if self.kwargs is not None else {}
if self.instance is None:
self.instance = self.klass(*new_args, **new_kwargs)
@@ -160,7 +160,7 @@
if proxy.kwargs is None or len(proxy.kwargs) == 0 or proxy.klass != klass:
continue
found = True
- for k,v in kwargs.items():
+ for k,v in kwargs.iteritems():
if (k not in proxy.kwargs) or v != proxy.kwargs[k]:
found = False
break
@@ -168,301 +168,6 @@
return proxy
return None
-class TwitterProcessorException(Exception):
- pass
-
-class TwitterProcessor(object):
-
- def __init__(self, json_dict, json_txt, source_id, session, access_token=None, token_filename=None, user_query_twitter=False):
-
- if json_dict is None and json_txt is None:
- raise TwitterProcessorException("No json")
-
- if json_dict is None:
- self.json_dict = anyjson.deserialize(json_txt)
- else:
- self.json_dict = json_dict
-
- if not json_txt:
- self.json_txt = anyjson.serialize(json_dict)
- else:
- self.json_txt = json_txt
-
- if "id" not in self.json_dict:
- raise TwitterProcessorException("No id in json")
-
- self.source_id = source_id
- self.session = session
- self.token_filename = token_filename
- self.access_token = access_token
- self.obj_buffer = ObjectsBuffer()
- self.user_query_twitter = user_query_twitter
-
-
-
- def __get_user(self, user_dict, do_merge):
- get_logger().debug("Get user : " + repr(user_dict)) #@UndefinedVariable
-
- user_dict = adapt_fields(user_dict, fields_adapter["stream"]["user"])
-
- user_id = user_dict.get("id",None)
- user_name = user_dict.get("screen_name", user_dict.get("name", None))
-
- if user_id is None and user_name is None:
- return None
-
- user = None
- if user_id:
- user = self.obj_buffer.get(User, id=user_id)
- else:
- user = self.obj_buffer.get(User, screen_name=user_name)
-
- #to do update user id needed
- if user is not None:
- user_created_at = None
- if user.args is not None:
- user_created_at = user.args.get('created_at', None)
- if user_created_at is None and user_dict.get('created_at', None) is not None and do_merge:
- if user.args is None:
- user.args = user_dict
- else:
- user.args.update(user_dict)
- return user
-
- #todo : add methpds to objectbuffer to get buffer user
- user_obj = None
- if user_id:
- user_obj = self.session.query(User).filter(User.id == user_id).first()
- else:
- user_obj = self.session.query(User).filter(User.screen_name.ilike(user_name)).first()
-
- #todo update user if needed
- if user_obj is not None:
- if user_obj.created_at is not None or user_dict.get('created_at', None) is None or not do_merge :
- user = ObjectBufferProxy(User, None, None, False, user_obj)
- else:
- user = self.obj_buffer.add_object(User, None, user_dict, True, user_obj)
- return user
-
- user_created_at = user_dict.get("created_at", None)
-
- if user_created_at is None and self.user_query_twitter:
-
- if self.access_token is not None:
- acess_token_key, access_token_secret = self.access_token
- else:
- acess_token_key, access_token_secret = get_oauth_token(self.token_filename)
- t = twitter.Twitter(auth=twitter.OAuth(acess_token_key, access_token_secret, CONSUMER_KEY, CONSUMER_SECRET))
- try:
- if user_id:
- user_dict = t.users.show(user_id=user_id)
- else:
- user_dict = t.users.show(screen_name=user_name)
- except Exception as e:
- get_logger().info("get_user : TWITTER ERROR : " + repr(e)) #@UndefinedVariable
- get_logger().info("get_user : TWITTER ERROR : " + str(e)) #@UndefinedVariable
- return None
-
- if "id" not in user_dict:
- return None
-
- #TODO filter get, wrap in proxy
- user_obj = self.session.query(User).filter(User.id == user_dict["id"]).first()
-
- if user_obj is not None and not do_merge:
- return ObjectBufferProxy(User, None, None, False, user_obj)
- else:
- return self.obj_buffer.add_object(User, None, user_dict, True)
-
- def __get_or_create_object(self, klass, filter_by_kwargs, filter_arg, creation_kwargs, must_flush, do_merge):
-
- obj_proxy = self.obj_buffer.get(klass, **filter_by_kwargs)
- if obj_proxy is None:
- query = self.session.query(klass)
- if filter_arg is not None:
- query = query.filter(filter_arg)
- else:
- query = query.filter_by(**filter_by_kwargs)
- obj_instance = query.first()
- if obj_instance is not None:
- if not do_merge:
- obj_proxy = ObjectBufferProxy(klass, None, None, False, obj_instance)
- else:
- obj_proxy = self.obj_buffer.add_object(klass, None, creation_kwargs, must_flush, obj_instance)
- if obj_proxy is None:
- obj_proxy = self.obj_buffer.add_object(klass, None, creation_kwargs, must_flush)
- return obj_proxy
-
-
- def __process_entity(self, ind, ind_type):
- get_logger().debug("Process_entity : " + repr(ind) + " : " + repr(ind_type)) #@UndefinedVariable
-
- ind = clean_keys(ind)
-
- entity_type = self.__get_or_create_object(EntityType, {'label':ind_type}, None, {'label':ind_type}, True, False)
-
- entity_dict = {
- "indice_start" : ind["indices"][0],
- "indice_end" : ind["indices"][1],
- "tweet_id" : self.tweet.id,
- "entity_type_id" : entity_type.id,
- "source" : adapt_json(ind)
- }
-
- def process_medias():
-
- media_id = ind.get('id', None)
- if media_id is None:
- return None, None
-
- type_str = ind.get("type", "photo")
- media_type = self.__get_or_create_object(MediaType, {'label': type_str}, None, {'label':type_str}, True, False)
- media_ind = adapt_fields(ind, fields_adapter["entities"]["medias"])
- if "type" in media_ind:
- del(media_ind["type"])
- media_ind['type_id'] = media_type.id
- media = self.__get_or_create_object(Media, {'id':media_id}, None, media_ind, True, False)
-
- entity_dict['media_id'] = media.id
- return EntityMedia, entity_dict
-
- def process_hashtags():
- text = ind.get("text", ind.get("hashtag", None))
- if text is None:
- return None, None
- ind['text'] = text
- hashtag = self.__get_or_create_object(Hashtag, {'text':text}, Hashtag.text.ilike(text), ind, True, False)
- entity_dict['hashtag_id'] = hashtag.id
- return EntityHashtag, entity_dict
-
- def process_user_mentions():
- user_mention = self.__get_user(ind, False)
- if user_mention is None:
- entity_dict['user_id'] = None
- else:
- entity_dict['user_id'] = user_mention.id
- return EntityUser, entity_dict
-
- def process_urls():
- url = self.__get_or_create_object(Url, {'url':ind["url"]}, None, ind, True, False)
- entity_dict['url_id'] = url.id
- return EntityUrl, entity_dict
-
- #{'': lambda }
- entity_klass, entity_dict = {
- 'hashtags': process_hashtags,
- 'user_mentions' : process_user_mentions,
- 'urls' : process_urls,
- 'media': process_medias,
- }.get(ind_type, lambda: (Entity, entity_dict))()
-
- get_logger().debug("Process_entity entity_dict: " + repr(entity_dict)) #@UndefinedVariable
- if entity_klass:
- self.obj_buffer.add_object(entity_klass, None, entity_dict, False)
-
-
- def __process_twitter_stream(self):
-
- tweet_nb = self.session.query(Tweet).filter(Tweet.id == self.json_dict["id"]).count()
- if tweet_nb > 0:
- return
-
- ts_copy = adapt_fields(self.json_dict, fields_adapter["stream"]["tweet"])
-
- # get or create user
- user = self.__get_user(self.json_dict["user"], True)
- if user is None:
- get_logger().warning("USER not found " + repr(self.json_dict["user"])) #@UndefinedVariable
- ts_copy["user_id"] = None
- else:
- ts_copy["user_id"] = user.id
-
- del(ts_copy['user'])
- ts_copy["tweet_source_id"] = self.source_id
-
- self.tweet = self.obj_buffer.add_object(Tweet, None, ts_copy, True)
-
- self.__process_entities()
-
-
- def __process_entities(self):
- if "entities" in self.json_dict:
- for ind_type, entity_list in self.json_dict["entities"].items():
- for ind in entity_list:
- self.__process_entity(ind, ind_type)
- else:
-
- text = self.tweet.text
- extractor = twitter_text.Extractor(text)
- for ind in extractor.extract_hashtags_with_indices():
- self.__process_entity(ind, "hashtags")
-
- for ind in extractor.extract_urls_with_indices():
- self.__process_entity(ind, "urls")
-
- for ind in extractor.extract_mentioned_screen_names_with_indices():
- self.__process_entity(ind, "user_mentions")
-
- def __process_twitter_rest(self):
- tweet_nb = self.session.query(Tweet).filter(Tweet.id == self.json_dict["id"]).count()
- if tweet_nb > 0:
- return
-
-
- tweet_fields = {
- 'created_at': self.json_dict["created_at"],
- 'favorited': False,
- 'id': self.json_dict["id"],
- 'id_str': self.json_dict["id_str"],
- #'in_reply_to_screen_name': ts["to_user"],
- 'in_reply_to_user_id': self.json_dict["to_user_id"],
- 'in_reply_to_user_id_str': self.json_dict["to_user_id_str"],
- #'place': ts["place"],
- 'source': self.json_dict["source"],
- 'text': self.json_dict["text"],
- 'truncated': False,
- 'tweet_source_id' : self.source_id,
- }
-
- #user
-
- user_fields = {
- 'lang' : self.json_dict.get('iso_language_code',None),
- 'profile_image_url' : self.json_dict["profile_image_url"],
- 'screen_name' : self.json_dict["from_user"],
- 'id' : self.json_dict["from_user_id"],
- 'id_str' : self.json_dict["from_user_id_str"],
- 'name' : self.json_dict['from_user_name'],
- }
-
- user = self.__get_user(user_fields, do_merge=False)
- if user is None:
- get_logger().warning("USER not found " + repr(user_fields)) #@UndefinedVariable
- tweet_fields["user_id"] = None
- else:
- tweet_fields["user_id"] = user.id
-
- tweet_fields = adapt_fields(tweet_fields, fields_adapter["rest"]["tweet"])
- self.tweet = self.obj_buffer.add_object(Tweet, None, tweet_fields, True)
-
- self.__process_entities()
-
-
-
- def process(self):
-
- if self.source_id is None:
- tweet_source = self.obj_buffer.add_object(TweetSource, None, {'original_json':self.json_txt}, True)
- self.source_id = tweet_source.id
-
- if "metadata" in self.json_dict:
- self.__process_twitter_rest()
- else:
- self.__process_twitter_stream()
-
- self.obj_buffer.add_object(TweetLog, None, {'tweet_source_id':self.source_id, 'status':TweetLog.TWEET_STATUS['OK']}, True)
-
- self.obj_buffer.persists(self.session)
def set_logging(options, plogger=None, queue=None):
@@ -507,12 +212,12 @@
return logger
def set_logging_options(parser):
- parser.add_option("-l", "--log", dest="logfile",
+ parser.add_argument("-l", "--log", dest="logfile",
help="log to file", metavar="LOG", default="stderr")
- parser.add_option("-v", dest="verbose", action="count",
- help="verbose", metavar="VERBOSE", default=0)
- parser.add_option("-q", dest="quiet", action="count",
- help="quiet", metavar="QUIET", default=0)
+ parser.add_argument("-v", dest="verbose", action="count",
+ help="verbose", default=0)
+ parser.add_argument("-q", dest="quiet", action="count",
+ help="quiet", default=0)
def get_base_query(session, query, start_date, end_date, hashtags, tweet_exclude_table, user_whitelist):
@@ -561,19 +266,17 @@
def get_logger():
global logger_name
- return logging.getLogger(logger_name) #@UndefinedVariable
+ return logging.getLogger(logger_name)
-# Next two import lines for this demo only
-
-class QueueHandler(logging.Handler): #@UndefinedVariable
+class QueueHandler(logging.Handler):
"""
This is a logging handler which sends events to a multiprocessing queue.
"""
def __init__(self, queue, ignore_full):
"""
- Initialise an instance, using the passed queue.
+ Initialize an instance, using the passed queue.
"""
logging.Handler.__init__(self) #@UndefinedVariable
self.queue = queue
@@ -588,10 +291,12 @@
try:
ei = record.exc_info
if ei:
- dummy = self.format(record) # just to get traceback text into record.exc_text
+ _ = self.format(record) # just to get traceback text into record.exc_text
record.exc_info = None # not needed any more
- if not self.ignore_full or not self.queue.full():
+ if not self.ignore_full or (not self.queue.full()):
self.queue.put_nowait(record)
+ except AssertionError:
+ pass
except Queue.Full:
if self.ignore_full:
pass
@@ -615,9 +320,9 @@
spaces = math.floor(width - marks)
loader = u'[' + (u'=' * int(marks)) + (u' ' * int(spaces)) + u']'
-
+
s = u"%s %3d%% %*d/%d - %*s\r" % (loader, percent, len(str(total_line)), current_line, total_line, width, label[:width])
-
+
writer.write(s) #takes the header into account
if percent >= 100:
writer.write("\n")
@@ -625,3 +330,10 @@
return writer
+def get_unused_port():
+ s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ s.bind(('localhost', 0))
+ _, port = s.getsockname()
+ s.close()
+ return port
+
--- a/script/lib/iri_tweet/setup.py Wed Apr 10 18:38:21 2013 +0200
+++ b/script/lib/iri_tweet/setup.py Fri Jun 07 11:55:46 2013 +0200
@@ -45,7 +45,7 @@
if line.strip() == '# -eof meta-':
break
acc.append(line)
- for pattern, handler in pats.items():
+ for pattern, handler in pats.iteritems():
m = pattern.match(line.strip())
if m:
meta.update(handler(m))
--- a/script/lib/tweetstream/CHANGELOG Wed Apr 10 18:38:21 2013 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,68 +0,0 @@
-0.1
-
- - Initial version
-
-0.2
-
- - Improved error handling
- - Added AuthenticationError and ConnectionError exceptions
- - Added ReconnectingTweetStream class that supports automatically
- reconnecting if the connection is dropped
-
-0.3
-
- - Fixed bugs in authtentication
- - Added TrackStream and FollowStream classes
- - Added list of endpoint names, and made them legal values for the url arg
-
-0.3.1
-
- - Added lots of tests
- - Added proper handling of keepalive newlines
- - Improved handling of closing streams
- - Added missing anyjson dependency to setup
- - Fixed bug where newlines and malformed content were counted as a tweet
-
-0.3.2
-
- - This release was skipped over, due to maintainer brainfart.
-
-0.3.3
-
- - Fixed setup.py so it wont attempt to load modules that aren't installed
- yet. Fixes installation issue.
-
-0.3.4
-
- - Updated to latest twitter streaming urls
- - Fixed a bug where we tried to call a method on None
-
-0.3.5
-
- - Removed a spurious print statement left over from debugging
- - Introduced common base class for all tweetstream exceptions
- - Make sure we raise a sensible error on 404. Include url in desc of that error
-
-0.3.6
-
- - Added LocationStream class for filtering on location bounding boxes.
-
-1.0.0
-
- - Changed API to match latest twitter endpoints. This adds SampleStream and
- FilterStream and deprecates TweetStream, FollowStream, LocationStream,
- TrackStream and ReconnectingTweetStream.
-
-1.1.0
-
- - Fixed issues #2 and #12, related to low volume streams not yielding tweets
- until a relatively large buffer was filled. This meant that tweets would
- arrive in bunches, not immediatly.
- - Switched to HTTPS urls for streams. Twitter will switch off HTTP streams
- on 29. sept. 2011.
- - Added support for Python 3
-
-1.1.1
-
- - Fixed issue #16. Odd case where python_version_tuple was returning ints
- rather than the strings the docs promis. Make sure we always cast to int.
--- a/script/lib/tweetstream/LICENSE Wed Apr 10 18:38:21 2013 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,27 +0,0 @@
-Copyright (c) 2009, Rune Halvorsen
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are met:
-
- * Redistributions of source code must retain the above copyright notice,
- this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-
-Neither the name of Rune Halvorsen nor the names of its contributors may be
-used to endorse or promote products derived from this software without
-specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
-THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
-BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGE.
--- a/script/lib/tweetstream/README Wed Apr 10 18:38:21 2013 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,110 +0,0 @@
-.. -*- restructuredtext -*-
-
-##########################################
-tweetstream - Simple twitter streaming API
-##########################################
-
-Introduction
-------------
-
-tweetstream provides two classes, SampleStream and FollowStream, that can be
-used to get tweets from Twitter's streaming API. An instance of one of the
-classes can be used as an iterator. In addition to fetching tweets, the
-object keeps track of the number of tweets collected and the rate at which
-tweets are received.
-
-SampleStream delivers a sample of all tweets. FilterStream delivers
-tweets that match one or more criteria. Note that it's not possible
-to get all tweets without access to the "firehose" stream, which
-is not currently avaliable to the public.
-
-Twitter's documentation about the streaming API can be found here:
-http://dev.twitter.com/pages/streaming_api_methods .
-
-**Note** that the API is blocking. If for some reason data is not immediatly
-available, calls will block until enough data is available to yield a tweet.
-
-Examples
---------
-
-Printing incoming tweets:
-
->>> stream = tweetstream.SampleStream("username", "password")
->>> for tweet in stream:
-... print tweet
-
-
-The stream object can also be used as a context, as in this example that
-prints the author for each tweet as well as the tweet count and rate:
-
->>> with tweetstream.SampleStream("username", "password") as stream
-... for tweet in stream:
-... print "Got tweet from %-16s\t( tweet %d, rate %.1f tweets/sec)" % (
-... tweet["user"]["screen_name"], stream.count, stream.rate )
-
-
-Stream objects can raise ConnectionError or AuthenticationError exceptions:
-
->>> try:
-... with tweetstream.TweetStream("username", "password") as stream
-... for tweet in stream:
-... print "Got tweet from %-16s\t( tweet %d, rate %.1f tweets/sec)" % (
-... tweet["user"]["screen_name"], stream.count, stream.rate )
-... except tweetstream.ConnectionError, e:
-... print "Disconnected from twitter. Reason:", e.reason
-
-To get tweets that match specific criteria, use the FilterStream. FilterStreams
-take three keyword arguments: "locations", "follow" and "track".
-
-Locations are a list of bounding boxes in which geotagged tweets should originate.
-The argument should be an iterable of longitude/latitude pairs.
-
-Track specifies keywords to track. The argument should be an iterable of
-strings.
-
-Follow returns statuses that reference given users. Argument should be an iterable
-of twitter user IDs. The IDs are userid ints, not the screen names.
-
->>> words = ["opera", "firefox", "safari"]
->>> people = [123,124,125]
->>> locations = ["-122.75,36.8", "-121.75,37.8"]
->>> with tweetstream.FilterStream("username", "password", track=words,
-... follow=people, locations=locations) as stream
-... for tweet in stream:
-... print "Got interesting tweet:", tweet
-
-
-Deprecated classes
-------------------
-
-tweetstream used to contain the classes TweetStream, FollowStream, TrackStream
-LocationStream and ReconnectingTweetStream. These were deprecated when twitter
-changed its API end points. The same functionality is now available in
-SampleStream and FilterStream. The deprecated methods will emit a warning when
-used, but will remain functional for a while longer.
-
-
-Changelog
----------
-
-See the CHANGELOG file
-
-Contact
--------
-
-The author is Rune Halvorsen <runefh@gmail.com>. The project resides at
-http://bitbucket.org/runeh/tweetstream . If you find bugs, or have feature
-requests, please report them in the project site issue tracker. Patches are
-also very welcome.
-
-Contributors
-------------
-
-- Rune Halvorsen
-- Christopher Schierkolk
-
-License
--------
-
-This software is licensed under the ``New BSD License``. See the ``LICENCE``
-file in the top distribution directory for the full license text.
--- a/script/lib/tweetstream/conftest.py Wed Apr 10 18:38:21 2013 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,10 +0,0 @@
-# content of conftest.py
-
-import pytest
-def pytest_addoption(parser):
- parser.addoption("--runslow", action="store_true",
- help="run slow tests")
-
-def pytest_runtest_setup(item):
- if 'slow' in item.keywords and not item.config.getvalue("runslow"):
- pytest.skip("need --runslow option to run")
\ No newline at end of file
--- a/script/lib/tweetstream/servercontext.py Wed Apr 10 18:38:21 2013 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,221 +0,0 @@
-import threading
-import contextlib
-import time
-import os
-import socket
-import random
-from functools import partial
-from inspect import isclass
-from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler
-from SimpleHTTPServer import SimpleHTTPRequestHandler
-from SocketServer import BaseRequestHandler
-
-
-class ServerError(Exception):
- pass
-
-
-class ServerContext(object):
- """Context object with information about a running test server."""
-
- def __init__(self, address, port):
- self.address = address or "localhost"
- self.port = port
-
- @property
- def baseurl(self):
- return "http://%s:%s" % (self.address, self.port)
-
- def __str__(self):
- return "<ServerContext %s >" % self.baseurl
-
- __repr__ = __str__
-
-
-class _SilentSimpleHTTPRequestHandler(SimpleHTTPRequestHandler):
-
- def __init__(self, *args, **kwargs):
- self.logging = kwargs.get("logging", False)
- SimpleHTTPRequestHandler.__init__(self, *args, **kwargs)
-
- def log_message(self, *args, **kwargs):
- if self.logging:
- SimpleHTTPRequestHandler.log_message(self, *args, **kwargs)
-
-
-class _TestHandler(BaseHTTPRequestHandler):
- """RequestHandler class that handles requests that use a custom handler
- callable."""
-
- def __init__(self, handler, methods, *args, **kwargs):
- self._handler = handler
- self._methods = methods
- self._response_sent = False
- self._headers_sent = False
- self.logging = kwargs.get("logging", False)
- BaseHTTPRequestHandler.__init__(self, *args, **kwargs)
-
- def log_message(self, *args, **kwargs):
- if self.logging:
- BaseHTTPRequestHandler.log_message(self, *args, **kwargs)
-
- def send_response(self, *args, **kwargs):
- self._response_sent = True
- BaseHTTPRequestHandler.send_response(self, *args, **kwargs)
-
- def end_headers(self, *args, **kwargs):
- self._headers_sent = True
- BaseHTTPRequestHandler.end_headers(self, *args, **kwargs)
-
- def _do_whatever(self):
- """Called in place of do_METHOD"""
- data = self._handler(self)
-
- if hasattr(data, "next"):
- # assume it's something supporting generator protocol
- self._handle_with_iterator(data)
- else:
- # Nothing more to do then.
- pass
-
-
- def __getattr__(self, name):
- if name.startswith("do_") and name[3:].lower() in self._methods:
- return self._do_whatever
- else:
- # fixme instance or class?
- raise AttributeError(name)
-
- def _handle_with_iterator(self, iterator):
- self.connection.settimeout(0.1)
- for data in iterator:
- if not self.server.server_thread.running:
- return
-
- if not self._response_sent:
- self.send_response(200)
- if not self._headers_sent:
- self.end_headers()
-
- self.wfile.write(data)
- # flush immediatly. We may want to do trickling writes
- # or something else tha trequires bypassing normal caching
- self.wfile.flush()
-
-class _TestServerThread(threading.Thread):
- """Thread class for a running test server"""
-
- def __init__(self, handler, methods, cwd, port, address):
- threading.Thread.__init__(self)
- self.startup_finished = threading.Event()
- self._methods = methods
- self._cwd = cwd
- self._orig_cwd = None
- self._handler = self._wrap_handler(handler, methods)
- self._setup()
- self.running = True
- self.serverloc = (address, port)
- self.error = None
-
- def _wrap_handler(self, handler, methods):
- if isclass(handler) and issubclass(handler, BaseRequestHandler):
- return handler # It's OK. user passed in a proper handler
- elif callable(handler):
- return partial(_TestHandler, handler, methods)
- # it's a callable, so wrap in a req handler
- else:
- raise ServerError("handler must be callable or RequestHandler")
-
- def _setup(self):
- if self._cwd != "./":
- self._orig_cwd = os.getcwd()
- os.chdir(self._cwd)
-
- def _init_server(self):
- """Hooks up the server socket"""
- try:
- if self.serverloc[1] == "random":
- retries = 10 # try getting an available port max this many times
- while True:
- try:
- self.serverloc = (self.serverloc[0],
- random.randint(1025, 49151))
- self._server = HTTPServer(self.serverloc, self._handler)
- except socket.error:
- retries -= 1
- if not retries: # not able to get a port.
- raise
- else:
- break
- else: # use specific port. this might throw, that's expected
- self._server = HTTPServer(self.serverloc, self._handler)
- except socket.error, e:
- self.running = False
- self.error = e
- # set this here, since we'll never enter the serve loop where
- # it is usually set:
- self.startup_finished.set()
- return
-
- self._server.allow_reuse_address = True # lots of tests, same port
- self._server.timeout = 0.1
- self._server.server_thread = self
-
-
- def run(self):
- self._init_server()
-
- while self.running:
- self._server.handle_request() # blocks for self.timeout secs
- # First time this falls through, signal the parent thread that
- # the server is ready for incomming connections
- if not self.startup_finished.is_set():
- self.startup_finished.set()
-
- self._cleanup()
-
- def stop(self):
- """Stop the server and attempt to make the thread terminate.
- This happens async but the calling code can check periodically
- the isRunning flag on the thread object.
- """
- # actual stopping happens in the run method
- self.running = False
-
- def _cleanup(self):
- """Do some rudimentary cleanup."""
- if self._orig_cwd:
- os.chdir(self._orig_cwd)
-
-
-@contextlib.contextmanager
-def test_server(handler=_SilentSimpleHTTPRequestHandler, port=8514,
- address="", methods=("get", "head"), cwd="./"):
- """Context that makes available a web server in a separate thread"""
- thread = _TestServerThread(handler=handler, methods=methods, cwd=cwd,
- port=port, address=address)
- thread.start()
-
- # fixme: should this be daemonized? If it isn't it will block the entire
- # app, but that should never happen anyway..
- thread.startup_finished.wait()
-
- if thread.error: # startup failed! Bail, throw whatever the server did
- raise thread.error
-
- exc = None
- try:
- yield ServerContext(*thread.serverloc)
- except Exception, exc:
- pass
- thread.stop()
- thread.join(5) # giving it a lot of leeway. should never happen
-
- if exc:
- raise exc
-
- # fixme: this takes second priorty after the internal exception but would
- # still be nice to signal back to calling code.
-
- if thread.isAlive():
- raise Warning("Test server could not be stopped")
--- a/script/lib/tweetstream/setup.py Wed Apr 10 18:38:21 2013 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,82 +0,0 @@
-#@PydevCodeAnalysisIgnore
-import sys
-import os
-
-extra = {}
-if sys.version_info >= (3, 0):
- extra.update(use_2to3=True)
-
-
-try:
- from setuptools import setup, find_packages
-except ImportError:
- from distutils.core import setup, find_packages
-
-
-# -*- Distribution Meta -*-
-import re
-re_meta = re.compile(r'__(\w+?)__\s*=\s*(.*)')
-re_vers = re.compile(r'VERSION\s*=\s*\((.*?)\)')
-re_doc = re.compile(r'^"""(.+?)"""', re.M|re.S)
-rq = lambda s: s.strip("\"'")
-
-
-def add_default(m):
- attr_name, attr_value = m.groups()
- return ((attr_name, rq(attr_value)), )
-
-
-def add_version(m):
- v = list(map(rq, m.groups()[0].split(", ")))
- return (("VERSION", ".".join(v[0:3]) + "".join(v[3:])), )
-
-
-def add_doc(m):
- return (("doc", m.groups()[0].replace("\n", " ")), )
-
-pats = {re_meta: add_default,
- re_vers: add_version}
-here = os.path.abspath(os.path.dirname(__file__))
-meta_fh = open(os.path.join(here, "tweetstream/__init__.py"))
-try:
- meta = {}
- acc = []
- for line in meta_fh:
- if line.strip() == '# -eof meta-':
- break
- acc.append(line)
- for pattern, handler in pats.items():
- m = pattern.match(line.strip())
- if m:
- meta.update(handler(m))
- m = re_doc.match("".join(acc).strip())
- if m:
- meta.update(add_doc(m))
-finally:
- meta_fh.close()
-
-
-setup(name='tweetstream',
- version=meta["VERSION"],
- description=meta["doc"],
- long_description=open("README").read(),
- classifiers=[
- 'License :: OSI Approved :: BSD License',
- 'Intended Audience :: Developers',
- 'Programming Language :: Python :: 2.6',
- 'Programming Language :: Python :: 2.7',
- 'Programming Language :: Python :: 3',
- 'Programming Language :: Python :: 3.1',
- ],
- keywords='twitter',
- author=meta["author"],
- author_email=meta["contact"],
- url=meta["homepage"],
- license='BSD',
- packages=find_packages(exclude=['ez_setup', 'examples', 'tests']),
- include_package_data=True,
- zip_safe=False,
- platforms=["any"],
- install_requires=['anyjson'],
- **extra
-)
--- a/script/lib/tweetstream/tests/test_tweetstream.py Wed Apr 10 18:38:21 2013 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,188 +0,0 @@
-import contextlib
-import threading
-import time
-
-from tweetstream import TweetStream, FollowStream, TrackStream, LocationStream
-from tweetstream import ConnectionError, AuthenticationError, SampleStream, FilterStream
-from tweepy.auth import BasicAuthHandler
-
-import pytest
-from pytest import raises
-slow = pytest.mark.slow
-
-from servercontext import test_server
-
-single_tweet = r"""{"in_reply_to_status_id":null,"in_reply_to_user_id":null,"favorited":false,"created_at":"Tue Jun 16 10:40:14 +0000 2009","in_reply_to_screen_name":null,"text":"record industry just keeps on amazing me: http:\/\/is.gd\/13lFo - $150k per song you've SHARED, not that somebody has actually DOWNLOADED.","user":{"notifications":null,"profile_background_tile":false,"followers_count":206,"time_zone":"Copenhagen","utc_offset":3600,"friends_count":191,"profile_background_color":"ffffff","profile_image_url":"http:\/\/s3.amazonaws.com\/twitter_production\/profile_images\/250715794\/profile_normal.png","description":"Digital product developer, currently at Opera Software. My tweets are my opinions, not those of my employer.","verified_profile":false,"protected":false,"favourites_count":0,"profile_text_color":"3C3940","screen_name":"eiriksnilsen","name":"Eirik Stridsklev N.","following":null,"created_at":"Tue May 06 12:24:12 +0000 2008","profile_background_image_url":"http:\/\/s3.amazonaws.com\/twitter_production\/profile_background_images\/10531192\/160x600opera15.gif","profile_link_color":"0099B9","profile_sidebar_fill_color":"95E8EC","url":"http:\/\/www.stridsklev-nilsen.no\/eirik","id":14672543,"statuses_count":506,"profile_sidebar_border_color":"5ED4DC","location":"Oslo, Norway"},"id":2190767504,"truncated":false,"source":"<a href=\"http:\/\/widgets.opera.com\/widget\/7206\">Twitter Opera widget<\/a>"}""" + "\r"
-
-
-def parameterized(funcarglist):
- def wrapper(function):
- function.funcarglist = funcarglist
- return function
- return wrapper
-
-def pytest_generate_tests(metafunc):
- for funcargs in getattr(metafunc.function, 'funcarglist', ()):
- metafunc.addcall(funcargs=funcargs)
-
-
-streamtypes = [
- dict(cls=TweetStream, args=[], kwargs=dict()),
- dict(cls=SampleStream, args=[], kwargs=dict()),
- dict(cls=FilterStream, args=[], kwargs=dict(track=("test",))),
- dict(cls=FollowStream, args=[[1, 2, 3]], kwargs=dict()),
- dict(cls=TrackStream, args=["opera"], kwargs=dict()),
- dict(cls=LocationStream, args=["123,4321"], kwargs=dict())
-]
-
-
-@parameterized(streamtypes)
-def test_bad_auth(cls, args, kwargs):
- """Test that the proper exception is raised when the user could not be
- authenticated"""
- def auth_denied(request):
- request.send_error(401)
-
- with raises(AuthenticationError):
- with test_server(handler=auth_denied, methods=("post", "get"), port="random") as server:
- auth = BasicAuthHandler("user", "passwd")
- stream = cls(auth, *args, url=server.baseurl)
- for e in stream: pass
-
-
-@parameterized(streamtypes)
-def test_404_url(cls, args, kwargs):
- """Test that the proper exception is raised when the stream URL can't be
- found"""
- def not_found(request):
- request.send_error(404)
-
- with raises(ConnectionError):
- with test_server(handler=not_found, methods=("post", "get"), port="random") as server:
- auth = BasicAuthHandler("user", "passwd")
- stream = cls(auth, *args, url=server.baseurl)
- for e in stream: pass
-
-
-@parameterized(streamtypes)
-def test_bad_content(cls, args, kwargs):
- """Test error handling if we are given invalid data"""
- def bad_content(request):
- for n in xrange(10):
- # what json we pass doesn't matter. It's not verifying the
- # strcuture, only checking that it's parsable
- yield "[1,2,3]\r"
- yield "[1,2, I need no stinking close brace\r"
- yield "[1,2,3]\r"
-
-
- with raises(ConnectionError):
- with test_server(handler=bad_content, methods=("post", "get"), port="random") as server:
- auth = BasicAuthHandler("user", "passwd")
- stream = cls(auth, *args, url=server.baseurl)
- for tweet in stream:
- pass
-
-
-@parameterized(streamtypes)
-def test_closed_connection(cls, args, kwargs):
- """Test error handling if server unexpectedly closes connection"""
- cnt = 1000
- def bad_content(request):
- for n in xrange(cnt):
- # what json we pass doesn't matter. It's not verifying the
- # strcuture, only checking that it's parsable
- yield "[1,2,3]\r"
-
- with raises(ConnectionError):
- with test_server(handler=bad_content, methods=("post", "get"), port="random") as server:
- auth = BasicAuthHandler("foo", "bar")
- stream = cls(auth, *args, url=server.baseurl)
- for tweet in stream:
- pass
-
-
-@parameterized(streamtypes)
-def test_bad_host(cls, args, kwargs):
- """Test behaviour if we can't connect to the host"""
- with raises(ConnectionError):
- stream = cls("username", "passwd", *args, url="http://wedfwecfghhreewerewads.foo")
- stream.next()
-
-
-@parameterized(streamtypes)
-def smoke_test_receive_tweets(cls, args, kwargs):
- """Receive 100k tweets and disconnect (slow)"""
- total = 100000
-
- def tweetsource(request):
- while True:
- yield single_tweet + "\n"
-
- with test_server(handler=tweetsource, methods=("post", "get"), port="random") as server:
- auth = BasicAuthHandler("foo", "bar")
- stream = cls(auth, *args, url=server.baseurl)
- for tweet in stream:
- if stream.count == total:
- break
-
-
-@parameterized(streamtypes)
-def test_keepalive(cls, args, kwargs):
- """Make sure we behave sanely when there are keepalive newlines in the
- data recevived from twitter"""
- def tweetsource(request):
- yield single_tweet+"\n"
- yield "\n"
- yield "\n"
- yield single_tweet+"\n"
- yield "\n"
- yield "\n"
- yield "\n"
- yield "\n"
- yield "\n"
- yield "\n"
- yield "\n"
- yield single_tweet+"\n"
- yield "\n"
-
-
- with test_server(handler=tweetsource, methods=("post", "get"), port="random") as server:
- auth = BasicAuthHandler("foo", "bar")
- stream = cls(auth, *args, url=server.baseurl)
- try:
- for tweet in stream:
- pass
- except ConnectionError:
- assert stream.count == 3, "Got %s, wanted 3" % stream.count
- else:
- assert False, "Didn't handle keepalive"
-
-
-@slow
-@parameterized(streamtypes)
-def test_buffering(cls, args, kwargs):
- """Test if buffering stops data from being returned immediately.
- If there is some buffering in play that might mean data is only returned
- from the generator when the buffer is full. If buffer is bigger than a
- tweet, this will happen. Default buffer size in the part of socket lib
- that enables readline is 8k. Max tweet length is around 3k."""
-
- def tweetsource(request):
- yield single_tweet+"\n"
- time.sleep(2)
- # need to yield a bunch here so we're sure we'll return from the
- # blocking call in case the buffering bug is present.
- for n in xrange(100):
- yield single_tweet+"\n"
-
-
- with test_server(handler=tweetsource, methods=("post", "get"), port="random") as server:
- auth = BasicAuthHandler("foo", "bar")
- stream = cls(auth, *args, url=server.baseurl)
- start = time.time()
- stream.next()
- first = time.time()
- diff = first - start
- assert diff < 1, "Getting first tweet took more than a second!"
-
--- a/script/lib/tweetstream/tox.ini Wed Apr 10 18:38:21 2013 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,16 +0,0 @@
-[tox]
-envlist = py25,py26,py27,py30,py31,py32
-
-[testenv]
-deps=pytest
-sitepackages=False
-commands=py.test --runslow
-
-[testenv:py30]
-changedir = .tox
-
-[testenv:py31]
-changedir = .tox
-
-[testenv:py32]
-changedir = .tox
\ No newline at end of file
--- a/script/lib/tweetstream/tweetstream/__init__.py Wed Apr 10 18:38:21 2013 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,45 +0,0 @@
-"""Simple access to Twitter's streaming API"""
-
-VERSION = (1, 1, 1, 'iri')
-__version__ = ".".join(map(str, VERSION[0:3])) + "".join(VERSION[3:])
-__author__ = "Rune Halvorsen"
-__contact__ = "runefh@gmail.com"
-__homepage__ = "http://bitbucket.org/runeh/tweetstream/"
-__docformat__ = "restructuredtext"
-
-# -eof meta-
-
-
-"""
- .. data:: USER_AGENT
-
- The default user agent string for stream objects
-"""
-
-USER_AGENT = "TweetStream %s" % __version__
-
-
-class TweetStreamError(Exception):
- """Base class for all tweetstream errors"""
- pass
-
-
-class AuthenticationError(TweetStreamError):
- """Exception raised if the username/password is not accepted"""
- pass
-
-
-class ConnectionError(TweetStreamError):
- """Raised when there are network problems. This means when there are
- dns errors, network errors, twitter issues"""
-
- def __init__(self, reason, details=None):
- self.reason = reason
- self.details = details
-
- def __str__(self):
- return '<ConnectionError %s>' % self.reason
-
-
-from .streamclasses import SampleStream, FilterStream
-from .deprecated import FollowStream, TrackStream, LocationStream, TweetStream, ReconnectingTweetStream
--- a/script/lib/tweetstream/tweetstream/deprecated.py Wed Apr 10 18:38:21 2013 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,82 +0,0 @@
-from .streamclasses import FilterStream, SampleStream, ConnectionError
-import time
-
-class DeprecatedStream(FilterStream):
- def __init__(self, *args, **kwargs):
- import warnings
- warnings.warn("%s is deprecated. Use FilterStream instead" % self.__class__.__name__, DeprecationWarning)
- super(DeprecatedStream, self).__init__(*args, **kwargs)
-
-
-class FollowStream(DeprecatedStream):
- def __init__(self, auth, follow, catchup=None, url=None):
- super(FollowStream, self).__init__(auth, follow=follow, catchup=catchup, url=url)
-
-
-class TrackStream(DeprecatedStream):
- def __init__(self, auth, track, catchup=None, url=None, slow=False):
- super(TrackStream, self).__init__(auth, track=track, catchup=catchup, url=url)
-
-
-class LocationStream(DeprecatedStream):
- def __init__(self, auth, locations, catchup=None, url=None, slow=False):
- super(LocationStream, self).__init__(auth, locations=locations, catchup=catchup, url=url)
-
-
-class TweetStream(SampleStream):
- def __init__(self, *args, **kwargs):
- import warnings
- warnings.warn("%s is deprecated. Use SampleStream instead" % self.__class__.__name__, DeprecationWarning)
- SampleStream.__init__(self, *args, **kwargs)
-
-
-class ReconnectingTweetStream(TweetStream):
- """TweetStream class that automatically tries to reconnect if the
- connecting goes down. Reconnecting, and waiting for reconnecting, is
- blocking.
-
- :param username: See :TweetStream:
-
- :param password: See :TweetStream:
-
- :keyword url: See :TweetStream:
-
- :keyword reconnects: Number of reconnects before a ConnectionError is
- raised. Default is 3
-
- :error_cb: Optional callable that will be called just before trying to
- reconnect. The callback will be called with a single argument, the
- exception that caused the reconnect attempt. Default is None
-
- :retry_wait: Time to wait before reconnecting in seconds. Default is 5
-
- """
-
- def __init__(self, auth, url="sample",
- reconnects=3, error_cb=None, retry_wait=5):
- self.max_reconnects = reconnects
- self.retry_wait = retry_wait
- self._reconnects = 0
- self._error_cb = error_cb
- TweetStream.__init__(self, auth, url=url)
-
- def next(self):
- while True:
- try:
- return TweetStream.next(self)
- except ConnectionError, e:
- self._reconnects += 1
- if self._reconnects > self.max_reconnects:
- raise ConnectionError("Too many retries")
-
- # Note: error_cb is not called on the last error since we
- # raise a ConnectionError instead
- if callable(self._error_cb):
- self._error_cb(e)
-
- time.sleep(self.retry_wait)
- # Don't listen to auth error, since we can't reasonably reconnect
- # when we get one.
-
-
-
--- a/script/lib/tweetstream/tweetstream/streamclasses.py Wed Apr 10 18:38:21 2013 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,256 +0,0 @@
-import time
-import urllib
-import urllib2
-import socket
-from platform import python_version_tuple
-import anyjson
-
-from . import AuthenticationError, ConnectionError, USER_AGENT
-
-class BaseStream(object):
- """A network connection to Twitters streaming API
-
- :param auth: tweepy auth object.
- :keyword catchup: Number of tweets from the past to get before switching to
- live stream.
- :keyword raw: If True, return each tweet's raw data direct from the socket,
- without UTF8 decoding or parsing, rather than a parsed object. The
- default is False.
- :keyword timeout: If non-None, set a timeout in seconds on the receiving
- socket. Certain types of network problems (e.g., disconnecting a VPN)
- can cause the connection to hang, leading to indefinite blocking that
- requires kill -9 to resolve. Setting a timeout leads to an orderly
- shutdown in these cases. The default is None (i.e., no timeout).
- :keyword url: Endpoint URL for the object. Note: you should not
- need to edit this. It's present to make testing easier.
-
- .. attribute:: connected
-
- True if the object is currently connected to the stream.
-
- .. attribute:: url
-
- The URL to which the object is connected
-
- .. attribute:: starttime
-
- The timestamp, in seconds since the epoch, the object connected to the
- streaming api.
-
- .. attribute:: count
-
- The number of tweets that have been returned by the object.
-
- .. attribute:: rate
-
- The rate at which tweets have been returned from the object as a
- float. see also :attr: `rate_period`.
-
- .. attribute:: rate_period
-
- The ammount of time to sample tweets to calculate tweet rate. By
- default 10 seconds. Changes to this attribute will not be reflected
- until the next time the rate is calculated. The rate of tweets vary
- with time of day etc. so it's usefull to set this to something
- sensible.
-
- .. attribute:: user_agent
-
- User agent string that will be included in the request. NOTE: This can
- not be changed after the connection has been made. This property must
- thus be set before accessing the iterator. The default is set in
- :attr: `USER_AGENT`.
- """
-
- def __init__(self, auth,
- catchup=None, raw=False, timeout=None, url=None):
- self._conn = None
- self._rate_ts = None
- self._rate_cnt = 0
- self._auth = auth
- self._catchup_count = catchup
- self._raw_mode = raw
- self._timeout = timeout
- self._iter = self.__iter__()
-
- self.rate_period = 10 # in seconds
- self.connected = False
- self.starttime = None
- self.count = 0
- self.rate = 0
- self.user_agent = USER_AGENT
- if url: self.url = url
-
- self.muststop = False
-
- def __enter__(self):
- return self
-
- def __exit__(self, *params):
- self.close()
- return False
-
- def _init_conn(self):
- """Open the connection to the twitter server"""
- headers = {'User-Agent': self.user_agent}
-
- postdata = self._get_post_data() or {}
- if self._catchup_count:
- postdata["count"] = self._catchup_count
-
- poststring = urllib.urlencode(postdata) if postdata else None
-
- if self._auth:
- self._auth.apply_auth(self.url, "POST", headers, postdata)
-
- req = urllib2.Request(self.url, poststring, headers)
-
- try:
- self._conn = urllib2.urlopen(req, timeout=self._timeout)
-
- except urllib2.HTTPError, exception:
- if exception.code == 401:
- raise AuthenticationError("Access denied")
- elif exception.code == 404:
- raise ConnectionError("URL not found: %s" % self.url)
- else: # re raise. No idea what would cause this, so want to know
- raise
- except urllib2.URLError, exception:
- raise ConnectionError(exception.reason)
-
- # This is horrible. This line grabs the raw socket (actually an ssl
- # wrapped socket) from the guts of urllib2/httplib. We want the raw
- # socket so we can bypass the buffering that those libs provide.
- # The buffering is reasonable when dealing with connections that
- # try to finish as soon as possible. With twitters' never ending
- # connections, it causes a bug where we would not deliver tweets
- # until the buffer was full. That's problematic for very low volume
- # filterstreams, since you might not see a tweet for minutes or hours
- # after they occured while the buffer fills.
- #
- # Oh, and the inards of the http libs are different things on in
- # py2 and 3, so need to deal with that. py3 libs do more of what I
- # want by default, but I wont do more special casing for it than
- # neccessary.
-
- major, _, _ = python_version_tuple()
- # The cast is needed because apparently some versions return strings
- # and some return ints.
- # On my ubuntu with stock 2.6 I get strings, which match the docs.
- # Someone reported the issue on 2.6.1 on macos, but that was
- # manually built, not the bundled one. Anyway, cast for safety.
- major = int(major)
- if major == 2:
- self._socket = self._conn.fp._sock.fp._sock
- else:
- self._socket = self._conn.fp.raw
- # our code that reads from the socket expects a method called recv.
- # py3 socket.SocketIO uses the name read, so alias it.
- self._socket.recv = self._socket.read
-
- self.connected = True
- if not self.starttime:
- self.starttime = time.time()
- if not self._rate_ts:
- self._rate_ts = time.time()
-
- def _get_post_data(self):
- """Subclasses that need to add post data to the request can override
- this method and return post data. The data should be in the format
- returned by urllib.urlencode."""
- return None
-
- def __muststop(self):
- if callable(self.muststop):
- return self.muststop()
- else:
- return self.muststop
-
- def _update_rate(self):
- rate_time = time.time() - self._rate_ts
- if not self._rate_ts or rate_time > self.rate_period:
- self.rate = self._rate_cnt / rate_time
- self._rate_cnt = 0
- self._rate_ts = time.time()
-
- def __iter__(self):
- buf = b""
- while True:
- try:
- if self.__muststop():
- raise StopIteration()
-
- if not self.connected:
- self._init_conn()
-
- buf += self._socket.recv(8192)
- if buf == b"": # something is wrong
- self.close()
- raise ConnectionError("Got entry of length 0. Disconnected")
- elif buf.isspace():
- buf = b""
- elif b"\r" not in buf: # not enough data yet. Loop around
- continue
-
- lines = buf.split(b"\r")
- buf = lines[-1]
- lines = lines[:-1]
-
- for line in lines:
- if (self._raw_mode):
- tweet = line
- else:
- line = line.decode("utf8")
- try:
- tweet = anyjson.deserialize(line)
- except ValueError, e:
- self.close()
- raise ConnectionError("Got invalid data from twitter", details=line)
-
- if 'text' in tweet:
- self.count += 1
- self._rate_cnt += 1
- self._update_rate()
- yield tweet
-
-
- except socket.error, e:
- self.close()
- raise ConnectionError("Server disconnected")
-
-
- def next(self):
- """Return the next available tweet. This call is blocking!"""
- return self._iter.next()
-
-
- def close(self):
- """
- Close the connection to the streaming server.
- """
- self.connected = False
- if self._conn:
- self._conn.close()
-
-
-class SampleStream(BaseStream):
- url = "https://stream.twitter.com/1/statuses/sample.json"
-
-
-class FilterStream(BaseStream):
- url = "https://stream.twitter.com/1/statuses/filter.json"
-
- def __init__(self, auth, follow=None, locations=None,
- track=None, catchup=None, url=None, raw=False, timeout=None):
- self._follow = follow
- self._locations = locations
- self._track = track
- # remove follow, locations, track
- BaseStream.__init__(self, auth, url=url, raw=raw, catchup=catchup, timeout=timeout)
-
- def _get_post_data(self):
- postdata = {}
- if self._follow: postdata["follow"] = ",".join([str(e) for e in self._follow])
- if self._locations: postdata["locations"] = ",".join(self._locations)
- if self._track: postdata["track"] = ",".join(self._track)
- return postdata
--- a/script/rest/enmi_profile.py Wed Apr 10 18:38:21 2013 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,134 +0,0 @@
-import twython
-from sqlite3 import *
-import datetime, time
-import email.utils
-from optparse import OptionParser
-import os.path
-import os
-import sys
-import simplejson
-
-
-#options filename rpp page total_pages start_date end_date
-
-
-
-def adapt_datetime(ts):
- return time.mktime(ts.timetuple())
-
-def adapt_geo(geo):
- return simplejson.dumps(geo)
-
-def convert_geo(s):
- return simplejson.loads(s)
-
-
-register_adapter(datetime.datetime, adapt_datetime)
-register_converter("geo", convert_geo)
-
-columns_tweet = [u'favorited', u'truncated', u'text', u'created_at', u'source', u'in_reply_to_status_id', u'in_reply_to_screen_name', u'in_reply_to_user_id', u'geo', u'id', u'user']
-columns_user = [u'id', u'verified', u'profile_sidebar_fill_color', u'profile_text_color', u'followers_count', u'protected', u'location', u'profile_background_color', u'utc_offset', u'statuses_count', u'description', u'friends_count', u'profile_link_color', u'profile_image_url', u'notifications', u'geo_enabled', u'profile_background_image_url', u'screen_name', u'profile_background_tile', u'favourites_count', u'name', u'url', u'created_at', u'time_zone', u'profile_sidebar_border_color', u'following']
-
-def processDate(entry):
- ts = email.utils.parsedate(entry["created_at"])
- entry["created_at_ts"] = datetime.datetime.fromtimestamp(time.mktime(ts))
-
-def processPage(page, cursor, debug):
- for entry in page:
- if debug:
- print "ENTRY : " + repr(entry)
- curs.execute("select id from tweet_tweet where id = ?", (entry["id"],))
- res = curs.fetchone()
- if res:
- continue
-
- entry_user = entry["user"]
- processDate(entry_user)
- cursor.execute("insert into tweet_user ("+",".join(entry_user.keys())+") values (:"+",:".join(entry_user.keys())+");", entry_user);
- new_id = cursor.lastrowid
- processDate(entry)
- entry["user"] = new_id
- if entry["geo"]:
- entry["geo"] = adapt_geo(entry["geo"])
- new_id = cursor.execute("insert into tweet_tweet ("+",".join(entry.keys())+") values (:"+",:".join(entry.keys())+");", entry);
-
-
-if __name__ == "__main__" :
-
- parser = OptionParser()
- parser.add_option("-f", "--file", dest="filename",
- help="write tweet to FILE", metavar="FILE", default="enmi2010_twitter_rest.db")
- parser.add_option("-r", "--rpp", dest="rpp",
- help="Results per page", metavar="RESULT_PER_PAGE", default=200, type='int')
- parser.add_option("-p", "--page", dest="page",
- help="page result", metavar="PAGE", default=1, type='int')
- parser.add_option("-t", "--total-page", dest="total_page",
- help="Total page number", metavar="TOTAL_PAGE", default=16, type='int')
- parser.add_option("-s", "--screenname", dest="screen_name",
- help="Twitter screen name", metavar="SCREEN_NAME")
- parser.add_option("-u", "--user", dest="username",
- help="Twitter user", metavar="USER", default=None)
- parser.add_option("-w", "--password", dest="password",
- help="Twitter password", metavar="PASSWORD", default=None)
- parser.add_option("-n", "--new", dest="new", action="store_true",
- help="new database", default=False)
- parser.add_option("-d", "--debug", dest="debug", action="store_true",
- help="debug", default=False)
-
-
-
- (options, args) = parser.parse_args()
-
- if options.debug:
- print "OPTIONS : "
- print repr(options)
-
- if options.screen_name is None:
- print "No Screen name. Exiting"
- sys.exit()
-
- if options.new and os.path.exists(options.filename):
- os.remove(options.filename)
-
- conn = connect(options.filename)
- conn.row_factory = Row
- curs = conn.cursor()
-
- curs.execute("create table if not exists tweet_user ("+ ",".join(columns_user) +", created_at_ts integer);")
-
- curs.execute("create table if not exists tweet_tweet ("+ ",".join(columns_tweet) +", created_at_ts integer);")
- curs.execute("create index if not exists id_index on tweet_tweet (id asc);");
-
- curs.execute("select count(*) from tweet_tweet;")
- res = curs.fetchone()
-
- old_total = res[0]
-
- twitter = twython.setup(username=options.username, password=options.password, headers="IRI enmi (python urllib)")
- twitter = twython.Twython(twitter_token = "54ThDZhpEjokcMgHJOMnQA", twitter_secret = "wUoL9UL2T87tfc97R0Dff2EaqRzpJ5XGdmaN2XK3udA")
-
- search_results = None
- page = options.page-1
-
- while (page < options.total_page and ( search_results is None or len(search_results) > 0)):
- page += 1
- try:
- search_results = twitter.getUserTimeline(screen_name=options.screen_name, count=options.rpp, page=page)
- except twython.TwythonError, (e):
- print "NAME : "+ options.screen_name + " ERROR : " + repr(e.msg)
- break
- print "NAME : "+ options.screen_name +" PAGE : " + repr(page) + " tweet: " + repr(len(search_results)) + " (total page : " + unicode(options.total_page) + " : rpp : "+unicode(options.rpp)+")"
- processPage(search_results, curs, options.debug)
-
- conn.commit()
-
- curs.execute("select count(*) from tweet_tweet;")
- res = curs.fetchone()
-
- total = res[0]
-
- print "Tweet for " + options.screen_name + " : " + unicode(total - old_total) +", Tweet total : " + repr(total)
-
- conn.close()
-
-
--- a/script/rest/export_twitter.py Wed Apr 10 18:38:21 2013 +0200
+++ b/script/rest/export_twitter.py Fri Jun 07 11:55:46 2013 +0200
@@ -1,16 +1,15 @@
#!/usr/bin/env python
# coding=utf-8
-from sqlite3 import *
+from sqlite3 import register_adapter, register_converter, connect, Row
import datetime, time
import email.utils
from optparse import OptionParser
import os.path
-import os
-import sys
from lxml import etree
import uuid
import re
+import simplejson
def parse_date(date_str):
ts = email.utils.parsedate_tz(date_str)
@@ -20,10 +19,10 @@
return time.mktime(ts.timetuple())
def adapt_geo(geo):
- return simplejson.dumps(geo)
-
+ return simplejson.dumps(geo)
+
def convert_geo(s):
- return simplejson.loads(s)
+ return simplejson.loads(s)
register_adapter(datetime.datetime, adapt_datetime)
@@ -73,7 +72,7 @@
ts = int(parse_date(options.start_date))
if options.end_date:
- te = int(parse_date(options.end_date))
+ te = int(parse_date(options.end_date))
else:
te = ts + options.duration
--- a/script/rest/getscreennames.py Wed Apr 10 18:38:21 2013 +0200
+++ b/script/rest/getscreennames.py Fri Jun 07 11:55:46 2013 +0200
@@ -1,11 +1,5 @@
-from sqlite3 import *
-import datetime, time
-import email.utils
from optparse import OptionParser
-import os.path
-import os
-import sys
-import simplejson
+from sqlite3 import connect, Row
import re
if __name__ == "__main__" :
--- a/script/rest/search_twitter.py Wed Apr 10 18:38:21 2013 +0200
+++ b/script/rest/search_twitter.py Fri Jun 07 11:55:46 2013 +0200
@@ -1,11 +1,9 @@
-from iri_tweet import models, utils
-from sqlalchemy.orm import sessionmaker
+from iri_tweet import models, processor, utils
+from optparse import OptionParser
import anyjson
-import sqlite3
+import re
+import sys
import twitter
-import re
-from optparse import OptionParser
-
def get_option():
@@ -23,16 +21,27 @@
help="Result per page")
parser.add_option("-t", dest="token_filename", metavar="TOKEN_FILENAME", default=".oauth_token",
help="Token file name")
+ parser.add_option("-k", "--key", dest="consumer_key",
+ help="Twitter consumer key", metavar="CONSUMER_KEY")
+ parser.add_option("-s", "--secret", dest="consumer_secret",
+ help="Twitter consumer secret", metavar="CONSUMER_SECRET")
+ return parser.parse_args()
- return parser.parse_args()
+def get_auth(options, access_token):
+ consumer_key = options.consumer_key
+ consumer_secret = options.consumer_secret
+ auth = twitter.OAuth(token=access_token[0], token_secret=access_token[1], consumer_key=consumer_key, consumer_secret=consumer_secret)
+ return auth
if __name__ == "__main__":
(options, args) = get_option()
+
+ access_token = utils.get_oauth_token(consumer_key=options.consumer_key, consumer_secret=options.consumer_secret, token_file_path=options.token_filename)
+ auth = get_auth(options, access_token)
- t = twitter.Twitter(domain="search.twitter.com")
- t.secure = False
+ t = twitter.Twitter(domain="api.twitter.com",api_version="1.1",secure=True, auth=auth)
conn_str = args[0].strip()
if not re.match("^\w+://.+", conn_str):
@@ -51,16 +60,26 @@
results = None
page = 1
print options.query
+
+ #get current_maxid
+ results = t.search.tweets(q=options.query, result_type="recent")
+ max_id = results.get('search_metadata',{}).get('max_id',0)
+ if max_id==0:
+ print("No results, exit")
+ sys.exit(0)
- while page <= int(1500/int(options.rpp)) and ( results is None or len(results) > 0):
- results = t.search(q=options.query, rpp=options.rpp, page=page, include_entities=True)
+ while page <= int(1500/int(options.rpp)) and \
+ ( results is None or len(results.get('statuses',0)) > 0) and \
+ max_id > 0:
+ results = t.search.tweets(q=options.query, count=options.rpp, max_id=max_id, include_entities=True, result_type='recent')
+ max_id = results.get('search_metadata',{}).get('since_id',1) - 1
- for tweet in results["results"]:
+ for tweet in results["statuses"]:
print tweet
tweet_str = anyjson.serialize(tweet)
#invalidate user id
- processor = utils.TwitterProcessor(tweet, tweet_str, None, session, None, options.token_filename)
- processor.process()
+ p = processor.TwitterProcessorStatus(json_dict=tweet, json_txt=tweet_str, source_id=None, session=session, consumer_token=(options.consumer_key, options.consumer_secret), access_token=access_token, token_filename=options.token_filename, user_query_twitter=False, logger=None)
+ p.process()
session.flush()
session.commit()
page += 1
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/script/stream/recorder_stream.py Fri Jun 07 11:55:46 2013 +0200
@@ -0,0 +1,603 @@
+from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
+from iri_tweet import models, utils
+from iri_tweet.models import TweetSource, TweetLog, ProcessEvent
+from iri_tweet.processor import get_processor
+from multiprocessing import Queue as mQueue, Process, Event
+from sqlalchemy.exc import OperationalError
+from sqlalchemy.orm import scoped_session
+import Queue
+import StringIO
+import anyjson
+import argparse
+import datetime
+import inspect
+import iri_tweet.stream
+import logging
+import os
+import re
+import requests_oauthlib
+import shutil
+import signal
+import socket
+import sqlalchemy.schema
+import sys
+import thread
+import threading
+import time
+import traceback
+import urllib2
+socket._fileobject.default_bufsize = 0
+
+
+
+# columns_tweet = [u'favorited', u'truncated', u'text', u'created_at', u'source', u'in_reply_to_status_id', u'in_reply_to_screen_name', u'in_reply_to_user_id', u'geo', u'id', u'user']
+columns_tweet = [u'user', u'favorited', u'contributors', u'truncated', u'text', u'created_at', u'retweeted', u'in_reply_to_status_id_str', u'coordinates', u'in_reply_to_user_id_str', u'entities', u'in_reply_to_status_id', u'place', u'in_reply_to_user_id', u'id', u'in_reply_to_screen_name', u'retweet_count', u'geo', u'id_str', u'source']
+# columns_user = [u'id', u'verified', u'profile_sidebar_fill_color', u'profile_text_color', u'followers_count', u'protected', u'location', u'profile_background_color', u'utc_offset', u'statuses_count', u'description', u'friends_count', u'profile_link_color', u'profile_image_url', u'notifications', u'geo_enabled', u'profile_background_image_url', u'screen_name', u'profile_background_tile', u'favourites_count', u'name', u'url', u'created_at', u'time_zone', u'profile_sidebar_border_color', u'following']
+columns_user = [u'follow_request_sent', u'profile_use_background_image', u'id', u'verified', u'profile_sidebar_fill_color', u'profile_text_color', u'followers_count', u'protected', u'location', u'profile_background_color', u'id_str', u'utc_offset', u'statuses_count', u'description', u'friends_count', u'profile_link_color', u'profile_image_url', u'notifications', u'show_all_inline_media', u'geo_enabled', u'profile_background_image_url', u'name', u'lang', u'following', u'profile_background_tile', u'favourites_count', u'screen_name', u'url', u'created_at', u'contributors_enabled', u'time_zone', u'profile_sidebar_border_color', u'is_translator', u'listed_count']
+# just put it in a sqlite3 tqble
+
+DEFAULT_TIMEOUT = 3
+
+class Requesthandler(BaseHTTPRequestHandler):
+
+ def __init__(self, request, client_address, server):
+ BaseHTTPRequestHandler.__init__(self, request, client_address, server)
+
+ def do_GET(self):
+ self.send_response(200)
+ self.end_headers()
+
+ def log_message(self, format, *args): # @ReservedAssignment
+ pass
+
+
+def set_logging(options):
+ loggers = []
+
+ loggers.append(utils.set_logging(options, logging.getLogger('iri.tweet')))
+ loggers.append(utils.set_logging(options, logging.getLogger('multiprocessing')))
+ if options.debug >= 2:
+ loggers.append(utils.set_logging(options, logging.getLogger('sqlalchemy.engine')))
+ # utils.set_logging(options, logging.getLogger('sqlalchemy.dialects'))
+ # utils.set_logging(options, logging.getLogger('sqlalchemy.pool'))
+ # utils.set_logging(options, logging.getLogger('sqlalchemy.orm'))
+ return loggers
+
+def set_logging_process(options, queue):
+ qlogger = utils.set_logging(options, logging.getLogger('iri.tweet.p'), queue)
+ qlogger.propagate = 0
+ return qlogger
+
+def get_auth(options, access_token):
+ consumer_key = options.consumer_key
+ consumer_secret = options.consumer_secret
+ auth = requests_oauthlib.OAuth1(client_key=consumer_key, client_secret=consumer_secret, resource_owner_key=access_token[0], resource_owner_secret=access_token[1], signature_type='auth_header')
+ return auth
+
+
+def add_process_event(event_type, args, session_maker):
+ session = session_maker()
+ try:
+ evt = ProcessEvent(args=None if args is None else anyjson.serialize(args), type=event_type)
+ session.add(evt)
+ session.commit()
+ finally:
+ session.close()
+
+
+class BaseProcess(Process):
+
+ def __init__(self, session_maker, queue, options, access_token, stop_event, logger_queue, parent_pid):
+ self.parent_pid = parent_pid
+ self.session_maker = session_maker
+ self.queue = queue
+ self.options = options
+ self.logger_queue = logger_queue
+ self.stop_event = stop_event
+ self.consumer_token = (options.consumer_key, options.consumer_secret)
+ self.access_token = access_token
+
+ super(BaseProcess, self).__init__()
+
+ #
+ # from http://stackoverflow.com/questions/2542610/python-daemon-doesnt-kill-its-kids
+ #
+ def parent_is_alive(self):
+ try:
+ # try to call Parent
+ os.kill(self.parent_pid, 0)
+ except OSError:
+ # *beeep* oh no! The phone's disconnected!
+ return False
+ else:
+ # *ring* Hi mom!
+ return True
+
+
+ def __get_process_event_args(self):
+ return {'name':self.name, 'pid':self.pid, 'parent_pid':self.parent_pid, 'options':self.options.__dict__, 'access_token':self.access_token}
+
+ def run(self):
+ try:
+ add_process_event("start_worker", self.__get_process_event_args(), self.session_maker)
+ self.do_run()
+ finally:
+ add_process_event("stop_worker", self.__get_process_event_args(), self.session_maker)
+
+ def do_run(self):
+ raise NotImplementedError()
+
+
+
+class SourceProcess(BaseProcess):
+
+ def __init__(self, session_maker, queue, options, access_token, stop_event, logger_queue, parent_pid):
+ self.track = options.track
+ self.token_filename = options.token_filename
+ self.timeout = options.timeout
+ self.stream = None
+ super(SourceProcess, self).__init__(session_maker, queue, options, access_token, stop_event, logger_queue, parent_pid)
+
+ def __source_stream_iter(self):
+
+ self.logger.debug("SourceProcess : run ")
+
+ self.logger.debug("SourceProcess : get_auth auth with option %s and token %s " %(self.options, self.access_token))
+ self.auth = get_auth(self.options, self.access_token)
+ self.logger.debug("SourceProcess : auth set ")
+
+ track_list = self.track # or raw_input('Keywords to track (comma seperated): ').strip()
+ self.logger.debug("SourceProcess : track list " + track_list)
+
+ track_list = [k.strip() for k in track_list.split(',')]
+
+ self.logger.debug("SourceProcess : before connecting to stream %s, url : %s, auth : %s" % (repr(track_list), self.options.url, repr(self.auth)))
+ self.stream = iri_tweet.stream.FilterStream(self.auth, track=track_list, raw=True, url=self.options.url, timeout=self.timeout, chunk_size=512, logger=self.logger)
+ self.logger.debug("SourceProcess : after connecting to stream")
+ self.stream.muststop = lambda: self.stop_event.is_set()
+
+ stream_wrapper = iri_tweet.stream.SafeStreamWrapper(self.stream, logger=self.logger)
+
+ session = self.session_maker()
+
+ #import pydevd
+ #pydevd.settrace(suspend=False)
+
+
+ try:
+ for tweet in stream_wrapper:
+ if not self.parent_is_alive():
+ self.stop_event.set()
+ sys.exit()
+ self.logger.debug("SourceProcess : tweet " + repr(tweet))
+ source = TweetSource(original_json=tweet)
+ self.logger.debug("SourceProcess : source created")
+ add_retries = 0
+ while add_retries < 10:
+ try:
+ add_retries += 1
+ session.add(source)
+ session.flush()
+ break
+ except OperationalError as e:
+ session.rollback()
+ self.logger.debug("SourceProcess : Operational Error %s nb %d" % (repr(e), add_retries))
+ if add_retries == 10:
+ raise
+
+ source_id = source.id
+ self.logger.debug("SourceProcess : before queue + source id " + repr(source_id))
+ self.logger.info("SourceProcess : Tweet count: %d - current rate : %.2f - running : %s" % (self.stream.count, self.stream.rate, int(time.time() - self.stream.starttime)))
+ session.commit()
+ self.queue.put((source_id, tweet), False)
+
+ except Exception as e:
+ self.logger.error("SourceProcess : Error when processing tweet " + repr(e))
+ raise
+ finally:
+ session.rollback()
+ session.close()
+ self.stream.close()
+ self.stream = None
+ if not self.stop_event.is_set():
+ self.stop_event.set()
+
+
+ def do_run(self):
+
+ self.logger = set_logging_process(self.options, self.logger_queue)
+
+ source_stream_iter_thread = threading.Thread(target=self.__source_stream_iter , name="SourceStreamIterThread")
+
+ source_stream_iter_thread.start()
+
+ try:
+ while not self.stop_event.is_set():
+ self.logger.debug("SourceProcess : In while after start")
+ self.stop_event.wait(DEFAULT_TIMEOUT)
+ except KeyboardInterrupt:
+ self.stop_event.set()
+ pass
+
+ if self.stop_event.is_set() and self.stream:
+ self.stream.close()
+ elif not self.stop_event.is_set() and not source_stream_iter_thread.is_alive:
+ self.stop_event.set()
+
+ self.queue.cancel_join_thread()
+ self.logger_queue.cancel_join_thread()
+ self.logger.info("SourceProcess : join")
+ source_stream_iter_thread.join(30)
+
+
+def process_tweet(tweet, source_id, session, consumer_token, access_token, twitter_query_user, token_filename, logger):
+ try:
+ if not tweet.strip():
+ return
+ tweet_obj = anyjson.deserialize(tweet)
+ processor_klass = get_processor(tweet_obj)
+ if not processor_klass:
+ tweet_log = TweetLog(tweet_source_id=source_id, status=TweetLog.TWEET_STATUS['NOT_TWEET'])
+ session.add(tweet_log)
+ return
+ processor = processor_klass(json_dict=tweet_obj,
+ json_txt=tweet,
+ source_id=source_id,
+ session=session,
+ consumer_token=consumer_token,
+ access_token=access_token,
+ token_filename=token_filename,
+ user_query_twitter=twitter_query_user,
+ logger=logger)
+ logger.info(processor.log_info())
+ logger.debug(u"Process_tweet :" + repr(tweet))
+ processor.process()
+
+ except ValueError as e:
+ message = u"Value Error %s processing tweet %s" % (repr(e), tweet)
+ output = StringIO.StringIO()
+ try:
+ traceback.print_exc(file=output)
+ error_stack = output.getvalue()
+ finally:
+ output.close()
+ tweet_log = TweetLog(tweet_source_id=source_id, status=TweetLog.TWEET_STATUS['NOT_TWEET'], error=message, error_stack=error_stack)
+ session.add(tweet_log)
+ session.commit()
+ except Exception as e:
+ message = u"Error %s processing tweet %s" % (repr(e), tweet)
+ logger.exception(message)
+ output = StringIO.StringIO()
+ try:
+ traceback.print_exc(file=output)
+ error_stack = output.getvalue()
+ finally:
+ output.close()
+ session.rollback()
+ tweet_log = TweetLog(tweet_source_id=source_id, status=TweetLog.TWEET_STATUS['ERROR'], error=message, error_stack=error_stack)
+ session.add(tweet_log)
+ session.commit()
+
+
+
+class TweetProcess(BaseProcess):
+
+ def __init__(self, session_maker, queue, options, access_token, stop_event, logger_queue, parent_pid):
+ super(TweetProcess, self).__init__(session_maker, queue, options, access_token, stop_event, logger_queue, parent_pid)
+ self.twitter_query_user = options.twitter_query_user
+
+
+ def do_run(self):
+
+ self.logger = set_logging_process(self.options, self.logger_queue)
+ session = self.session_maker()
+ try:
+ while not self.stop_event.is_set() and self.parent_is_alive():
+ try:
+ source_id, tweet_txt = self.queue.get(True, 3)
+ self.logger.debug("Processing source id " + repr(source_id))
+ except Exception as e:
+ self.logger.debug('Process tweet exception in loop : ' + repr(e))
+ continue
+ process_tweet(tweet_txt, source_id, session, self.consumer_token, self.access_token, self.twitter_query_user, self.options.token_filename, self.logger)
+ session.commit()
+ except KeyboardInterrupt:
+ self.stop_event.set()
+ finally:
+ session.rollback()
+ session.close()
+
+
+def get_sessionmaker(conn_str):
+ engine, metadata, Session = models.setup_database(conn_str, echo=False, create_all=False, autocommit=False)
+ Session = scoped_session(Session)
+ return Session, engine, metadata
+
+
+def process_leftovers(session, consumer_token, access_token, twitter_query_user, token_filename, ask_process_leftovers, logger):
+
+ sources = session.query(TweetSource).outerjoin(TweetLog).filter(TweetLog.id == None)
+ sources_count = sources.count()
+
+ if sources_count > 10 and ask_process_leftovers:
+ resp = raw_input("Do you want to process leftovers (Y/n) ? (%d tweet to process)" % sources_count)
+ if resp and resp.strip().lower() == "n":
+ return
+ logger.info("Process leftovers, %d tweets to process" % (sources_count))
+ for src in sources:
+ tweet_txt = src.original_json
+ process_tweet(tweet_txt, src.id, session, consumer_token, access_token, twitter_query_user, token_filename, logger)
+ session.commit()
+
+
+def process_log(logger_queues, stop_event):
+ while not stop_event.is_set():
+ for lqueue in logger_queues:
+ try:
+ record = lqueue.get_nowait()
+ logging.getLogger(record.name).handle(record)
+ except Queue.Empty:
+ continue
+ except IOError:
+ continue
+ time.sleep(0.1)
+
+
+def get_options():
+
+ usage = "usage: %(prog)s [options]"
+
+ parser = argparse.ArgumentParser(usage=usage)
+
+ parser.add_argument("-f", "--file", dest="conn_str",
+ help="write tweet to DATABASE. This is a connection string", metavar="CONNECTION_STR", default="enmi2010_twitter.db")
+ parser.add_argument("-T", "--track", dest="track",
+ help="Twitter track", metavar="TRACK")
+ parser.add_argument("-k", "--key", dest="consumer_key",
+ help="Twitter consumer key", metavar="CONSUMER_KEY", required=True)
+ parser.add_argument("-s", "--secret", dest="consumer_secret",
+ help="Twitter consumer secret", metavar="CONSUMER_SECRET", required=True)
+ parser.add_argument("-n", "--new", dest="new", action="store_true",
+ help="new database", default=False)
+ parser.add_argument("-D", "--daemon", dest="daemon", action="store_true",
+ help="launch daemon", default=False)
+ parser.add_argument("-t", dest="token_filename", metavar="TOKEN_FILENAME", default=".oauth_token",
+ help="Token file name")
+ parser.add_argument("-d", "--duration", dest="duration",
+ help="Duration of recording in seconds", metavar="DURATION", default= -1, type=int)
+ parser.add_argument("-N", "--nb-process", dest="process_nb",
+ help="number of process.\nIf 0, only the lefovers of the database are processed.\nIf 1, no postprocessing is done on the tweets.", metavar="PROCESS_NB", default=2, type=int)
+ parser.add_argument("--url", dest="url",
+ help="The twitter url to connect to.", metavar="URL", default=iri_tweet.stream.FilterStream.url)
+ parser.add_argument("--query-user", dest="twitter_query_user", action="store_true",
+ help="Query twitter for users", default=False)
+ parser.add_argument("--timeout", dest="timeout",
+ help="timeout for connecting in seconds", default=60, metavar="TIMEOUT", type=int)
+ parser.add_argument("--ask-process-leftovers", dest="ask_process_leftovers", action="store_false",
+ help="ask process leftover", default=True)
+
+
+ utils.set_logging_options(parser)
+
+ return parser.parse_args()
+
+
+def do_run(options, session_maker):
+
+ stop_args = {}
+
+ consumer_token = (options.consumer_key, options.consumer_secret)
+ access_token = utils.get_oauth_token(consumer_key=consumer_token[0], consumer_secret=consumer_token[1], token_file_path=options.token_filename)
+
+
+ session = session_maker()
+ try:
+ process_leftovers(session, consumer_token, access_token, options.twitter_query_user, options.token_filename, options.ask_process_leftovers, utils.get_logger())
+ session.commit()
+ finally:
+ session.rollback()
+ session.close()
+
+ if options.process_nb <= 0:
+ utils.get_logger().debug("Leftovers processed. Exiting.")
+ return None
+
+ queue = mQueue()
+ stop_event = Event()
+
+ # workaround for bug on using urllib2 and multiprocessing
+ httpd = HTTPServer(('127.0.0.1',0), Requesthandler)
+ thread.start_new_thread(httpd.handle_request, ())
+
+ req = urllib2.Request('http://localhost:%d' % httpd.server_port)
+ conn = None
+ try:
+ conn = urllib2.urlopen(req)
+ except:
+ utils.get_logger().debug("could not open localhost")
+ # donothing
+ finally:
+ if conn is not None:
+ conn.close()
+
+ process_engines = []
+ logger_queues = []
+
+ SessionProcess, engine_process, _ = get_sessionmaker(conn_str)
+ process_engines.append(engine_process)
+ lqueue = mQueue(50)
+ logger_queues.append(lqueue)
+ pid = os.getpid()
+ sprocess = SourceProcess(SessionProcess, queue, options, access_token, stop_event, lqueue, pid)
+
+ tweet_processes = []
+
+ for i in range(options.process_nb - 1):
+ SessionProcess, engine_process, _ = get_sessionmaker(conn_str)
+ process_engines.append(engine_process)
+ lqueue = mQueue(50)
+ logger_queues.append(lqueue)
+ cprocess = TweetProcess(SessionProcess, queue, options, access_token, stop_event, lqueue, pid)
+ tweet_processes.append(cprocess)
+
+ log_thread = threading.Thread(target=process_log, name="loggingThread", args=(logger_queues, stop_event,))
+ log_thread.daemon = True
+
+ log_thread.start()
+
+ sprocess.start()
+ for cprocess in tweet_processes:
+ cprocess.start()
+
+ add_process_event("pid", {'main':os.getpid(), 'source':(sprocess.name, sprocess.pid), 'consumers':dict([(p.name, p.pid) for p in tweet_processes])}, session_maker)
+
+ if options.duration >= 0:
+ end_ts = datetime.datetime.utcnow() + datetime.timedelta(seconds=options.duration)
+
+ def interupt_handler(signum, frame):
+ utils.get_logger().debug("shutdown asked " + repr(signum) + " " + repr(inspect.getframeinfo(frame, 9)))
+ stop_args.update({'message': 'interupt', 'signum':signum, 'frameinfo':inspect.getframeinfo(frame, 9)})
+ stop_event.set()
+
+ signal.signal(signal.SIGINT , interupt_handler)
+ signal.signal(signal.SIGHUP , interupt_handler)
+ signal.signal(signal.SIGALRM, interupt_handler)
+ signal.signal(signal.SIGTERM, interupt_handler)
+
+
+ while not stop_event.is_set():
+ if options.duration >= 0 and datetime.datetime.utcnow() >= end_ts:
+ stop_args.update({'message': 'duration', 'duration' : options.duration, 'end_ts' : end_ts})
+ stop_event.set()
+ break
+ if sprocess.is_alive():
+ utils.get_logger().debug("Source process alive")
+ time.sleep(1)
+ else:
+ stop_args.update({'message': 'Source process killed'})
+ stop_event.set()
+ break
+ utils.get_logger().debug("Joining Source Process")
+ try:
+ sprocess.join(10)
+ except:
+ utils.get_logger().debug("Pb joining Source Process - terminating")
+ finally:
+ sprocess.terminate()
+
+ for i, cprocess in enumerate(tweet_processes):
+ utils.get_logger().debug("Joining consumer process Nb %d" % (i + 1))
+ try:
+ cprocess.join(3)
+ except:
+ utils.get_logger().debug("Pb joining consumer process Nb %d - terminating" % (i + 1))
+ cprocess.terminate()
+
+
+ utils.get_logger().debug("Close queues")
+ try:
+ queue.close()
+ for lqueue in logger_queues:
+ lqueue.close()
+ except Exception as e:
+ utils.get_logger().error("error when closing queues %s", repr(e))
+ # do nothing
+
+
+ if options.process_nb > 1:
+ utils.get_logger().debug("Processing leftovers")
+ session = session_maker()
+ try:
+ process_leftovers(session, consumer_token, access_token, options.twitter_query_user, options.token_filename, options.ask_process_leftovers, utils.get_logger())
+ session.commit()
+ finally:
+ session.rollback()
+ session.close()
+
+ for pengine in process_engines:
+ pengine.dispose()
+
+ return stop_args
+
+
+def main(options):
+
+ global conn_str
+
+ conn_str = options.conn_str.strip()
+ if not re.match("^\w+://.+", conn_str):
+ conn_str = 'sqlite:///' + options.conn_str
+
+ if conn_str.startswith("sqlite") and options.new:
+ filepath = conn_str[conn_str.find(":///") + 4:]
+ if os.path.exists(filepath):
+ i = 1
+ basename, extension = os.path.splitext(filepath)
+ new_path = '%s.%d%s' % (basename, i, extension)
+ while i < 1000000 and os.path.exists(new_path):
+ i += 1
+ new_path = '%s.%d%s' % (basename, i, extension)
+ if i >= 1000000:
+ raise Exception("Unable to find new filename for " + filepath)
+ else:
+ shutil.move(filepath, new_path)
+
+ Session, engine, metadata = get_sessionmaker(conn_str)
+
+ if options.new:
+ check_metadata = sqlalchemy.schema.MetaData(bind=engine)
+ check_metadata.reflect()
+ if len(check_metadata.sorted_tables) > 0:
+ message = "Database %s not empty exiting" % conn_str
+ utils.get_logger().error(message)
+ sys.exit(message)
+
+ metadata.create_all(engine)
+ session = Session()
+ try:
+ models.add_model_version(session)
+ finally:
+ session.close()
+
+ stop_args = {}
+ try:
+ add_process_event(event_type="start", args={'options':options.__dict__, 'args': [], 'command_line': sys.argv}, session_maker=Session)
+ stop_args = do_run(options, Session)
+ except Exception as e:
+ utils.get_logger().exception("Error in main thread")
+ outfile = StringIO.StringIO()
+ try:
+ traceback.print_exc(file=outfile)
+ stop_args = {'error': repr(e), 'message': getattr(e, 'message', ''), 'stacktrace':outfile.getvalue()}
+ finally:
+ outfile.close()
+ raise
+ finally:
+ add_process_event(event_type="shutdown", args=stop_args, session_maker=Session)
+
+ utils.get_logger().debug("Done. Exiting. " + repr(stop_args))
+
+
+
+if __name__ == '__main__':
+
+ options = get_options()
+
+ loggers = set_logging(options)
+
+ utils.get_logger().debug("OPTIONS : " + repr(options))
+
+ if options.daemon:
+ options.ask_process_leftovers = False
+ import daemon
+
+ hdlr_preserve = []
+ for logger in loggers:
+ hdlr_preserve.extend([h.stream for h in logger.handlers])
+
+ context = daemon.DaemonContext(working_directory=os.getcwd(), files_preserve=hdlr_preserve)
+ with context:
+ main(options)
+ else:
+ main(options)
+
--- a/script/stream/recorder_tweetstream.py Wed Apr 10 18:38:21 2013 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,528 +0,0 @@
-from getpass import getpass
-from iri_tweet import models, utils
-from iri_tweet.models import TweetSource, TweetLog, ProcessEvent
-from multiprocessing import (Queue as mQueue, JoinableQueue, Process, Event,
- get_logger)
-from optparse import OptionParser
-from sqlalchemy.exc import OperationalError
-from sqlalchemy.orm import scoped_session
-import Queue
-import StringIO
-import anyjson
-import datetime
-import inspect
-import logging
-import os
-import re
-import shutil
-import signal
-import socket
-import sqlalchemy.schema
-import sys
-import threading
-import time
-import traceback
-import tweepy.auth
-import tweetstream
-import urllib2
-socket._fileobject.default_bufsize = 0
-
-
-
-#columns_tweet = [u'favorited', u'truncated', u'text', u'created_at', u'source', u'in_reply_to_status_id', u'in_reply_to_screen_name', u'in_reply_to_user_id', u'geo', u'id', u'user']
-columns_tweet = [u'user', u'favorited', u'contributors', u'truncated', u'text', u'created_at', u'retweeted', u'in_reply_to_status_id_str', u'coordinates', u'in_reply_to_user_id_str', u'entities', u'in_reply_to_status_id', u'place', u'in_reply_to_user_id', u'id', u'in_reply_to_screen_name', u'retweet_count', u'geo', u'id_str', u'source']
-#columns_user = [u'id', u'verified', u'profile_sidebar_fill_color', u'profile_text_color', u'followers_count', u'protected', u'location', u'profile_background_color', u'utc_offset', u'statuses_count', u'description', u'friends_count', u'profile_link_color', u'profile_image_url', u'notifications', u'geo_enabled', u'profile_background_image_url', u'screen_name', u'profile_background_tile', u'favourites_count', u'name', u'url', u'created_at', u'time_zone', u'profile_sidebar_border_color', u'following']
-columns_user = [u'follow_request_sent', u'profile_use_background_image', u'id', u'verified', u'profile_sidebar_fill_color', u'profile_text_color', u'followers_count', u'protected', u'location', u'profile_background_color', u'id_str', u'utc_offset', u'statuses_count', u'description', u'friends_count', u'profile_link_color', u'profile_image_url', u'notifications', u'show_all_inline_media', u'geo_enabled', u'profile_background_image_url', u'name', u'lang', u'following', u'profile_background_tile', u'favourites_count', u'screen_name', u'url', u'created_at', u'contributors_enabled', u'time_zone', u'profile_sidebar_border_color', u'is_translator', u'listed_count']
-#just put it in a sqlite3 tqble
-
-
-def set_logging(options):
- loggers = []
-
- loggers.append(utils.set_logging(options, logging.getLogger('iri.tweet')))
- loggers.append(utils.set_logging(options, logging.getLogger('multiprocessing')))
- if options.debug >= 2:
- loggers.append(utils.set_logging(options, logging.getLogger('sqlalchemy.engine')))
- #utils.set_logging(options, logging.getLogger('sqlalchemy.dialects'))
- #utils.set_logging(options, logging.getLogger('sqlalchemy.pool'))
- #utils.set_logging(options, logging.getLogger('sqlalchemy.orm'))
- return loggers
-
-def set_logging_process(options, queue):
- qlogger = utils.set_logging(options, logging.getLogger('iri.tweet.p'), queue)
- qlogger.propagate = 0
- return qlogger
-
-def get_auth(options, access_token):
- if options.username and options.password:
- auth = tweepy.auth.BasicAuthHandler(options.username, options.password)
- else:
- consumer_key = models.CONSUMER_KEY
- consumer_secret = models.CONSUMER_SECRET
- auth = tweepy.auth.OAuthHandler(consumer_key, consumer_secret, secure=False)
- auth.set_access_token(*access_token)
- return auth
-
-
-def add_process_event(type, args, session_maker):
- session = session_maker()
- try:
- evt = ProcessEvent(args=None if args is None else anyjson.serialize(args), type=type)
- session.add(evt)
- session.commit()
- finally:
- session.close()
-
-
-class BaseProcess(Process):
-
- def __init__(self, session_maker, queue, options, access_token, stop_event, logger_queue, parent_pid):
- self.parent_pid = parent_pid
- self.session_maker = session_maker
- self.queue = queue
- self.options = options
- self.logger_queue = logger_queue
- self.stop_event = stop_event
- self.access_token = access_token
-
- super(BaseProcess, self).__init__()
-
- #
- # from http://stackoverflow.com/questions/2542610/python-daemon-doesnt-kill-its-kids
- #
- def parent_is_alive(self):
- try:
- # try to call Parent
- os.kill(self.parent_pid, 0)
- except OSError:
- # *beeep* oh no! The phone's disconnected!
- return False
- else:
- # *ring* Hi mom!
- return True
-
-
- def __get_process_event_args(self):
- return {'name':self.name, 'pid':self.pid, 'parent_pid':self.parent_pid, 'options':self.options.__dict__, 'access_token':self.access_token}
-
- def run(self):
- try:
- add_process_event("start_worker", self.__get_process_event_args(), self.session_maker)
- self.do_run()
- finally:
- add_process_event("stop_worker", self.__get_process_event_args(), self.session_maker)
-
- def do_run(self):
- raise NotImplementedError()
-
-
-
-class SourceProcess(BaseProcess):
-
- def __init__(self, session_maker, queue, options, access_token, stop_event, logger_queue, parent_pid):
- self.track = options.track
- self.token_filename = options.token_filename
- self.catchup = options.catchup
- self.timeout = options.timeout
- super(SourceProcess, self).__init__(session_maker, queue, options, access_token, stop_event, logger_queue, parent_pid)
-
- def do_run(self):
-
- #import pydevd
- #pydevd.settrace(suspend=True)
-
- self.logger = set_logging_process(self.options, self.logger_queue)
- self.auth = get_auth(self.options, self.access_token)
-
- self.logger.debug("SourceProcess : run ")
- track_list = self.track # or raw_input('Keywords to track (comma seperated): ').strip()
- self.logger.debug("SourceProcess : track list " + track_list)
-
- track_list = [k.strip() for k in track_list.split(',')]
-
- self.logger.debug("SourceProcess : before connecting to stream " + repr(track_list))
- stream = tweetstream.FilterStream(self.auth, track=track_list, raw=True, url=self.options.url, catchup=self.catchup, timeout=self.timeout)
- self.logger.debug("SourceProcess : after connecting to stream")
- stream.muststop = lambda: self.stop_event.is_set()
-
- session = self.session_maker()
-
- try:
- for tweet in stream:
- if not self.parent_is_alive():
- sys.exit()
- self.logger.debug("SourceProcess : tweet " + repr(tweet))
- source = TweetSource(original_json=tweet)
- self.logger.debug("SourceProcess : source created")
- add_retries = 0
- while add_retries < 10:
- try:
- add_retries += 1
- session.add(source)
- session.flush()
- break
- except OperationalError as e:
- session.rollback()
- self.logger.debug("SourceProcess : Operational Error %s nb %d" % (repr(e), add_retries))
- if add_retries == 10:
- raise e
-
- source_id = source.id
- self.logger.debug("SourceProcess : before queue + source id " + repr(source_id))
- self.logger.info("SourceProcess : Tweet count: %d - current rate : %.2f - running : %s" % (stream.count, stream.rate, int(time.time() - stream.starttime)))
- session.commit()
- self.queue.put((source_id, tweet), False)
-
- except Exception as e:
- self.logger.error("SourceProcess : Error when processing tweet " + repr(e))
- finally:
- session.rollback()
- stream.close()
- session.close()
- self.queue.close()
- self.stop_event.set()
-
-
-def process_tweet(tweet, source_id, session, access_token, twitter_query_user, logger):
- try:
- tweet_obj = anyjson.deserialize(tweet)
- if 'text' not in tweet_obj:
- tweet_log = TweetLog(tweet_source_id=source_id, status=TweetLog.TWEET_STATUS['NOT_TWEET'])
- session.add(tweet_log)
- return
- screen_name = ""
- if 'user' in tweet_obj and 'screen_name' in tweet_obj['user']:
- screen_name = tweet_obj['user']['screen_name']
- logger.info(u"Process_tweet from %s : %s" % (screen_name, tweet_obj['text']))
- logger.debug(u"Process_tweet :" + repr(tweet))
- processor = utils.TwitterProcessor(tweet_obj, tweet, source_id, session, access_token, None, twitter_query_user)
- processor.process()
- except Exception as e:
- message = u"Error %s processing tweet %s" % (repr(e), tweet)
- logger.exception(message)
- output = StringIO.StringIO()
- try:
- traceback.print_exc(file=output)
- error_stack = output.getvalue()
- finally:
- output.close()
- session.rollback()
- tweet_log = TweetLog(tweet_source_id=source_id, status=TweetLog.TWEET_STATUS['ERROR'], error=message, error_stack=error_stack)
- session.add(tweet_log)
- session.commit()
-
-
-
-class TweetProcess(BaseProcess):
-
- def __init__(self, session_maker, queue, options, access_token, stop_event, logger_queue, parent_pid):
- super(TweetProcess, self).__init__(session_maker, queue, options, access_token, stop_event, logger_queue, parent_pid)
- self.twitter_query_user = options.twitter_query_user
-
-
- def do_run(self):
-
- self.logger = set_logging_process(self.options, self.logger_queue)
- session = self.session_maker()
- try:
- while not self.stop_event.is_set() and self.parent_is_alive():
- try:
- source_id, tweet_txt = self.queue.get(True, 3)
- self.logger.debug("Processing source id " + repr(source_id))
- except Exception as e:
- self.logger.debug('Process tweet exception in loop : ' + repr(e))
- continue
- process_tweet(tweet_txt, source_id, session, self.access_token, self.twitter_query_user, self.logger)
- session.commit()
- finally:
- session.rollback()
- self.stop_event.set()
- session.close()
-
-
-def get_sessionmaker(conn_str):
- engine, metadata, Session = models.setup_database(conn_str, echo=False, create_all=False, autocommit=False)
- Session = scoped_session(Session)
- return Session, engine, metadata
-
-
-def process_leftovers(session, access_token, twitter_query_user, logger):
-
- sources = session.query(TweetSource).outerjoin(TweetLog).filter(TweetLog.id == None)
-
- for src in sources:
- tweet_txt = src.original_json
- process_tweet(tweet_txt, src.id, session, access_token, twitter_query_user, logger)
- session.commit()
-
-
-
- #get tweet source that do not match any message
- #select * from tweet_tweet_source ts left join tweet_tweet_log tl on ts.id = tl.tweet_source_id where tl.id isnull;
-def process_log(logger_queues, stop_event):
- while not stop_event.is_set():
- for lqueue in logger_queues:
- try:
- record = lqueue.get_nowait()
- logging.getLogger(record.name).handle(record)
- except Queue.Empty:
- continue
- except IOError:
- continue
- time.sleep(0.1)
-
-
-def get_options():
-
- usage = "usage: %prog [options]"
-
- parser = OptionParser(usage=usage)
-
- parser.add_option("-f", "--file", dest="conn_str",
- help="write tweet to DATABASE. This is a connection string", metavar="CONNECTION_STR", default="enmi2010_twitter.db")
- parser.add_option("-u", "--user", dest="username",
- help="Twitter user", metavar="USER", default=None)
- parser.add_option("-w", "--password", dest="password",
- help="Twitter password", metavar="PASSWORD", default=None)
- parser.add_option("-T", "--track", dest="track",
- help="Twitter track", metavar="TRACK")
- parser.add_option("-n", "--new", dest="new", action="store_true",
- help="new database", default=False)
- parser.add_option("-D", "--daemon", dest="daemon", action="store_true",
- help="launch daemon", default=False)
- parser.add_option("-t", dest="token_filename", metavar="TOKEN_FILENAME", default=".oauth_token",
- help="Token file name")
- parser.add_option("-d", "--duration", dest="duration",
- help="Duration of recording in seconds", metavar="DURATION", default= -1, type='int')
- parser.add_option("-N", "--nb-process", dest="process_nb",
- help="number of process.\nIf 0, only the lefovers of the database are processed.\nIf 1, no postprocessing is done on the tweets.", metavar="PROCESS_NB", default=2, type='int')
- parser.add_option("--url", dest="url",
- help="The twitter url to connect to.", metavar="URL", default=tweetstream.FilterStream.url)
- parser.add_option("--query-user", dest="twitter_query_user", action="store_true",
- help="Query twitter for users", default=False, metavar="QUERY_USER")
- parser.add_option("--catchup", dest="catchup",
- help="catchup count for tweets", default=None, metavar="CATCHUP", type='int')
- parser.add_option("--timeout", dest="timeout",
- help="timeout for connecting in seconds", default=60, metavar="TIMEOUT", type='int')
-
-
-
-
- utils.set_logging_options(parser)
-
- return parser.parse_args()
-
-
-def do_run(options, session_maker):
-
- stop_args = {}
-
- access_token = None
- if not options.username or not options.password:
- access_token = utils.get_oauth_token(options.token_filename)
-
- session = session_maker()
- try:
- process_leftovers(session, access_token, options.twitter_query_user, utils.get_logger())
- session.commit()
- finally:
- session.rollback()
- session.close()
-
- if options.process_nb <= 0:
- utils.get_logger().debug("Leftovers processed. Exiting.")
- return None
-
- queue = mQueue()
- stop_event = Event()
-
- #workaround for bug on using urllib2 and multiprocessing
- req = urllib2.Request('http://localhost')
- conn = None
- try:
- conn = urllib2.urlopen(req)
- except:
- utils.get_logger().debug("could not open localhost")
- #donothing
- finally:
- if conn is not None:
- conn.close()
-
- process_engines = []
- logger_queues = []
-
- SessionProcess, engine_process, metadata_process = get_sessionmaker(conn_str)
- process_engines.append(engine_process)
- lqueue = mQueue(1)
- logger_queues.append(lqueue)
- pid = os.getpid()
- sprocess = SourceProcess(SessionProcess, queue, options, access_token, stop_event, lqueue, pid)
-
- tweet_processes = []
-
- for i in range(options.process_nb - 1):
- SessionProcess, engine_process, metadata_process = get_sessionmaker(conn_str)
- process_engines.append(engine_process)
- lqueue = mQueue(1)
- logger_queues.append(lqueue)
- cprocess = TweetProcess(SessionProcess, queue, options, access_token, stop_event, lqueue, pid)
- tweet_processes.append(cprocess)
-
- def interupt_handler(signum, frame):
- utils.get_logger().debug("shutdown asked " + repr(signum) + " " + repr(inspect.getframeinfo(frame, 9)))
- stop_args.update({'message': 'interupt', 'signum':signum, 'frameinfo':inspect.getframeinfo(frame, 9)})
- stop_event.set()
-
- signal.signal(signal.SIGINT , interupt_handler)
- signal.signal(signal.SIGHUP , interupt_handler)
- signal.signal(signal.SIGALRM, interupt_handler)
- signal.signal(signal.SIGTERM, interupt_handler)
-
- log_thread = threading.Thread(target=process_log, name="loggingThread", args=(logger_queues, stop_event,))
- log_thread.daemon = True
-
- log_thread.start()
-
- sprocess.start()
- for cprocess in tweet_processes:
- cprocess.start()
-
- add_process_event("pid", {'main':os.getpid(), 'source':(sprocess.name, sprocess.pid), 'consumers':dict([(p.name, p.pid) for p in tweet_processes])}, session_maker)
-
- if options.duration >= 0:
- end_ts = datetime.datetime.utcnow() + datetime.timedelta(seconds=options.duration)
-
-
- while not stop_event.is_set():
- if options.duration >= 0 and datetime.datetime.utcnow() >= end_ts:
- stop_args.update({'message': 'duration', 'duration' : options.duration, 'end_ts' : end_ts})
- stop_event.set()
- break
- if sprocess.is_alive():
- time.sleep(1)
- else:
- stop_args.update({'message': 'Source process killed'})
- stop_event.set()
- break
- utils.get_logger().debug("Joining Source Process")
- try:
- sprocess.join(10)
- except:
- utils.get_logger().debug("Pb joining Source Process - terminating")
- sprocess.terminate()
-
- for i, cprocess in enumerate(tweet_processes):
- utils.get_logger().debug("Joining consumer process Nb %d" % (i + 1))
- try:
- cprocess.join(3)
- except:
- utils.get_logger().debug("Pb joining consumer process Nb %d - terminating" % (i + 1))
- cprocess.terminate()
-
-
- utils.get_logger().debug("Close queues")
- try:
- queue.close()
- for lqueue in logger_queues:
- lqueue.close()
- except exception as e:
- utils.get_logger().error("error when closing queues %s", repr(e))
- #do nothing
-
-
- if options.process_nb > 1:
- utils.get_logger().debug("Processing leftovers")
- session = session_maker()
- try:
- process_leftovers(session, access_token, options.twitter_query_user, utils.get_logger())
- session.commit()
- finally:
- session.rollback()
- session.close()
-
- for pengine in process_engines:
- pengine.dispose()
-
- return stop_args
-
-
-def main(options, args):
-
- global conn_str
-
- conn_str = options.conn_str.strip()
- if not re.match("^\w+://.+", conn_str):
- conn_str = 'sqlite:///' + options.conn_str
-
- if conn_str.startswith("sqlite") and options.new:
- filepath = conn_str[conn_str.find(":///") + 4:]
- if os.path.exists(filepath):
- i = 1
- basename, extension = os.path.splitext(filepath)
- new_path = '%s.%d%s' % (basename, i, extension)
- while i < 1000000 and os.path.exists(new_path):
- i += 1
- new_path = '%s.%d%s' % (basename, i, extension)
- if i >= 1000000:
- raise Exception("Unable to find new filename for " + filepath)
- else:
- shutil.move(filepath, new_path)
-
- Session, engine, metadata = get_sessionmaker(conn_str)
-
- if options.new:
- check_metadata = sqlalchemy.schema.MetaData(bind=engine, reflect=True)
- if len(check_metadata.sorted_tables) > 0:
- message = "Database %s not empty exiting" % conn_str
- utils.get_logger().error(message)
- sys.exit(message)
-
- metadata.create_all(engine)
- session = Session()
- try:
- models.add_model_version(session)
- finally:
- session.close()
-
- stop_args = {}
- try:
- add_process_event(type="start", args={'options':options.__dict__, 'args': args, 'command_line': sys.argv}, session_maker=Session)
- stop_args = do_run(options, Session)
- except Exception as e:
- utils.get_logger().exception("Error in main thread")
- outfile = StringIO.StringIO()
- try:
- traceback.print_exc(file=outfile)
- stop_args = {'error': repr(e), 'message': getattr(e, 'message', ''), 'stacktrace':outfile.getvalue()}
- finally:
- outfile.close()
- raise
- finally:
- add_process_event(type="shutdown", args=stop_args, session_maker=Session)
-
- utils.get_logger().debug("Done. Exiting. " + repr(stop_args))
-
-
-
-if __name__ == '__main__':
-
- (options, args) = get_options()
-
- loggers = set_logging(options)
-
- utils.get_logger().debug("OPTIONS : " + repr(options))
-
- if options.daemon:
- import daemon
- import lockfile
-
- hdlr_preserve = []
- for logger in loggers:
- hdlr_preserve.extend([h.stream for h in logger.handlers])
-
- context = daemon.DaemonContext(working_directory=os.getcwd(), files_preserve=hdlr_preserve)
- with context:
- main(options, args)
- else:
- main(options, args)
-
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/script/utils/export_pad.py Fri Jun 07 11:55:46 2013 +0200
@@ -0,0 +1,320 @@
+#!/usr/bin/env python
+# coding=utf-8
+
+from dateutil.parser import parse as parse_date
+from iri_tweet.utils import set_logging_options, set_logging, get_logger
+from lxml import etree
+from optparse import OptionParser
+import anyjson
+import datetime
+import functools
+import httplib2
+import os.path
+import requests
+import sys
+import time
+import uuid
+
+
+class EtherpadRequestException(Exception):
+ def __init__(self, original_resp):
+ super(EtherpadRequestException, self).__init__(original_resp["message"])
+ self.status = original_resp["status"]
+ self.original_resp = original_resp
+
+
+class EtherpadRequest():
+
+ def __init__(self, base_url, api_key):
+ self.base_url = base_url
+ self.api_key = api_key
+ self.__request = None
+
+ def __getattr__(self, name):
+ return functools.partial(self.__action, name)
+
+ def __action(self, action, **kwargs):
+ url = "%s/%s" % (self.base_url, action)
+ params = dict(kwargs)
+ params['apikey'] = self.api_key
+
+ r = requests.get(url, params)
+
+ resp = anyjson.deserialize(r.text)
+
+ if resp["code"] == 0:
+ return resp["data"]
+ else:
+ raise EtherpadRequestException(resp)
+
+ return resp
+
+ def getRevisionsCount(self, padID):
+ f = self.__getattr__("getRevisionsCount")
+ res = f(padID=padID)
+
+ return res["revisions"]
+
+ def getPadUrl(self, padID):
+
+ return "%s/%s" % (self.base_url,padID)
+
+
+
+def abort(message, parser):
+ if message is not None:
+ sys.stderr.write(message + "\n")
+ parser.print_help()
+ sys.exit(1)
+
+def get_options():
+
+ parser = OptionParser()
+ parser.add_option("-u", "--api-url", dest="api_url",
+ help="Base etherpad-lite api url", metavar="API_URL", default=None)
+ parser.add_option("-k", "--api-key", dest="api_key",
+ help="Base etherpad-lite api url", metavar="API_KEY", default=None)
+ parser.add_option("-p", "--pad-id", dest="pad_id",
+ help="pad id", metavar="PADID")
+ parser.add_option("-s", "--start-date", dest="start_date",
+ help="start date", metavar="START_DATE", default=None)
+ parser.add_option("-e", "--end-date", dest="end_date",
+ help="end date", metavar="END_DATE", default=None)
+ parser.add_option("-f", "--format", dest="format", type="choice",
+ help="format", metavar="FORMAT", choice=['html', 'text'], default='html')
+ parser.add_option("-I", "--content-file", dest="content_file",
+ help="Content file", metavar="CONTENT_FILE")
+ parser.add_option("-C", "--color", dest="color",
+ help="Color code", metavar="COLOR", default="16763904")
+ parser.add_option("-D", "--duration", dest="duration", type="int",
+ help="Duration", metavar="DURATION", default=None)
+ parser.add_option("-n", "--name", dest="name",
+ help="Cutting name", metavar="NAME", default=u"pads")
+ parser.add_option("-R", "--replace", dest="replace", action="store_true",
+ help="Replace tweet ensemble", metavar="REPLACE", default=False)
+ parser.add_option("-m", "--merge", dest="merge", action="store_true",
+ help="merge tweet ensemble, choose the first ensemble", metavar="MERGE", default=False)
+ parser.add_option("-E", "--extended", dest="extended_mode", action="store_true",
+ help="Trigger polemic extended mode", metavar="EXTENDED", default=False)
+ parser.add_option("-S", "--step", dest="step", type=1,
+ help="step for version", metavar="STEP", default=False)
+
+
+
+ set_logging_options(parser)
+
+
+ return parser.parse_args() + (parser,)
+
+
+if __name__ == "__main__" :
+
+ (options, args, parser) = get_options()
+
+ set_logging(options)
+ get_logger().debug("OPTIONS : " + repr(options)) #@UndefinedVariable
+
+ if len(sys.argv) == 1:
+ abort(None)
+
+ base_url = options.get("api_url", None)
+ if not base_url:
+ abort("No base url")
+
+ api_key = options.get("api_key", None)
+ if not api_key:
+ abort("api key missing")
+
+ pad_id = options.get("pad_id", None)
+ if not pad_id:
+ abort("No pad id")
+
+ start_date_str = options.get("start_date",None)
+ end_date_str = options.get("end_date", None)
+ duration = options.get("duration", None)
+
+ start_date = None
+ start_ts = None
+ if start_date_str:
+ start_date = parse_date(start_date_str)
+ start_ts = time.mktime(start_date.timetuple())*1000
+
+ end_date = None
+ if end_date_str:
+ end_date = parse_date(end_date_str)
+ elif start_date and duration:
+ end_date = start_date + datetime.timedelta(seconds=duration)
+
+ if start_date is None or end_date is None:
+ abort("No start date found")
+
+ end_ts = None
+ if end_date is not None:
+ end_ts = time.mktime(end_date.timetuple())*1000
+
+ content_file = options.get("content_file", None)
+
+ if not content_file:
+ abort("No content file")
+
+ root = None
+
+ if content_file.find("http") == 0:
+
+ get_logger().debug("url : " + content_file) #@UndefinedVariable
+
+ h = httplib2.Http()
+ resp, content = h.request(content_file)
+
+ get_logger().debug("url response " + repr(resp) + " content " + repr(content)) #@UndefinedVariable
+
+ project = anyjson.deserialize(content)
+ root = etree.fromstring(project["ldt"])
+
+ elif os.path.exists(content_file):
+
+ doc = etree.parse(content_file)
+ root = doc.getroot()
+
+ if root is None:
+ abort("No content file, file not found")
+
+ cutting_name = options.get("name", None)
+ if cutting_name is None:
+ cutting_name = "pad_%s" % pad_id
+
+ output_format = options.get('format','html')
+ ensemble_parent = None
+
+ file_type = None
+ for node in root:
+ if node.tag == "project":
+ file_type = "ldt"
+ break
+ elif node.tag == "head":
+ file_type = "iri"
+ break
+ if file_type is None:
+ abort("Unknown file type")
+
+ if file_type == "ldt":
+ media_nodes = root.xpath("//media")
+ if len(media_nodes) > 0:
+ media = media_nodes[0]
+ annotations_node = root.find(u"annotations")
+ if annotations_node is None:
+ annotations_node = etree.SubElement(root, u"annotations")
+ content_node = annotations_node.find(u"content")
+ if content_node is None:
+ content_node = etree.SubElement(annotations_node,u"content", id=media.get(u"id"))
+ ensemble_parent = content_node
+ elif file_type == "iri":
+ body_node = root.find(u"body")
+ if body_node is None:
+ body_node = etree.SubElement(root, u"body")
+ ensembles_node = body_node.find(u"ensembles")
+ if ensembles_node is None:
+ ensembles_node = etree.SubElement(body_node, u"ensembles")
+ ensemble_parent = ensembles_node
+
+ if ensemble_parent is None:
+ abort("Can not add cutting")
+
+ if options.replace:
+ for ens in ensemble_parent.iterchildren(tag=u"ensemble"):
+ if ens.get("id","").startswith(cutting_name):
+ ensemble_parent.remove(ens)
+
+ ensemble = None
+ elements = None
+
+ if options.merge:
+ ensemble = ensemble_parent.find(u"ensemble")
+ if ensemble is not None:
+ elements = ensemble.find(u".//elements")
+
+ if ensemble is None or elements is None:
+ ensemble = etree.SubElement(ensemble_parent, u"ensemble", {u"id":u"tweet_" + unicode(uuid.uuid4()), u"title":u"Ensemble pad", u"author":u"IRI Web", u"abstract":u"Ensemble Pad"})
+ decoupage = etree.SubElement(ensemble, u"decoupage", {u"id": unicode(uuid.uuid4()), u"author": u"IRI Web"})
+
+ etree.SubElement(decoupage, u"title").text = unicode(cutting_name)
+ etree.SubElement(decoupage, u"abstract").text = unicode(cutting_name)
+
+ elements = etree.SubElement(decoupage, u"elements")
+
+
+ etp_req = EtherpadRequest(base_url, api_key)
+ rev_count = etp_req.getRevisionCount(pad_id)
+
+
+ version_range = range(1,rev_count+1, 1)
+ #make sure that teh last version is exported
+ if rev_count not in version_range:
+ version_range.append(rev_count)
+ for rev in version_range:
+
+ data = None
+ text = ""
+
+ if output_format == "html":
+ data = etp_req.getHtml(padID=pad_id, rev=rev)
+ text = data.get("html", "")
+ else:
+ data = etp_req.getText(padID=pad_id, rev=rev)
+ text = data.get("text","")
+
+ pad_ts = data['timestamp']
+
+ if pad_ts < start_ts:
+ continue
+
+ if end_ts is not None and pad_ts > end_ts:
+ break
+
+ pad_dt = datetime.datetime.fromtimestamp(float(pad_ts)/1000.0)
+ pad_ts_rel = pad_ts - start_ts
+
+ username = None
+ color = ""
+ if 'author' in data:
+ username = data['author']['name'] if ('name' in data['author'] and data['author']['name']) else data['author']['id']
+ color = data['author']['color'] if ('color' in data['author'] and data['author']['color']) else ""
+
+ if not username:
+ username = "anon."
+
+
+ element = etree.SubElement(elements, u"element" , {u"id":"%s-%s-%d" %(unicode(uuid.uuid4()),unicode(pad_id),rev), u"color":unicode(color), u"author":unicode(username), u"date":unicode(pad_dt.strftime("%Y/%m/%d")), u"begin": unicode(pad_ts_rel), u"dur":u"0", u"src":""})
+ etree.SubElement(element, u"title").text = "%s: %s - rev %d" % (unicode(username), unicode(pad_id), rev)
+ etree.SubElement(element, u"abstract").text = unicode(text)
+
+ meta_element = etree.SubElement(element, u'meta')
+ etree.SubElement(meta_element, "pad_url").text = etree.CDATA(unicode(etp_req.getPadUrl(pad_id)))
+ etree.SubElement(meta_element, "revision").text = etree.CDATA(unicode(rev))
+
+ # sort by tc in
+ if options.merge :
+ elements[:] = sorted(elements,key=lambda n: int(n.get('begin')))
+
+ output_data = etree.tostring(root, encoding="utf-8", method="xml", pretty_print=False, xml_declaration=True)
+
+ if content_file and content_file.find("http") == 0:
+
+ project["ldt"] = output_data
+ body = anyjson.serialize(project)
+ h = httplib2.Http()
+ resp, content = h.request(content_file, "PUT", headers={'content-type':'application/json'}, body=body)
+ if resp.status != 200:
+ raise Exception("Error writing content : %d : %s"%(resp.status, resp.reason))
+ else:
+ if content_file and os.path.exists(content_file):
+ dest_file_name = content_file
+ else:
+ dest_file_name = options.filename
+
+ output = open(dest_file_name, "w")
+ output.write(output_data)
+ output.flush()
+ output.close()
+
+
--- a/script/utils/export_tweet_db.py Wed Apr 10 18:38:21 2013 +0200
+++ b/script/utils/export_tweet_db.py Fri Jun 07 11:55:46 2013 +0200
@@ -1,8 +1,11 @@
-from models import setup_database
-from optparse import OptionParser #@UnresolvedImport
-from sqlalchemy.orm import sessionmaker
-from utils import set_logging_options, set_logging, TwitterProcessor, logger
-import sqlite3 #@UnresolvedImport
+from iri_tweet.models import setup_database
+from iri_tweet.processor import TwitterProcessorStatus
+from iri_tweet.utils import set_logging_options, set_logging
+from optparse import OptionParser
+import logging
+import sqlite3
+
+logger = logging.getLogger(__name__)
# 'entities': "tweet_entity",
@@ -33,7 +36,7 @@
fields_mapping = {}
for i,res in enumerate(curs_in.execute("select json from tweet_tweet;")):
logger.debug("main loop %d : %s" % (i, res[0])) #@UndefinedVariable
- processor = TwitterProcessor(eval(res[0]), res[0], None, session, options.token_filename)
+ processor = TwitterProcessorStatus(json_dict=eval(res[0]), json_txt=res[0], source_id=None, session=session, consumer_token=None, access_token=None, token_filename=options.token_filename, user_query_twitter=False, logger=logger)
processor.process()
session.commit()
logger.debug("main : %d tweet processed" % (i+1)) #@UndefinedVariable
--- a/script/utils/export_twitter_alchemy.py Wed Apr 10 18:38:21 2013 +0200
+++ b/script/utils/export_twitter_alchemy.py Fri Jun 07 11:55:46 2013 +0200
@@ -366,7 +366,7 @@
username = None
profile_url = ""
if tw.user is not None:
- username = tw.user.name
+ username = tw.user.screen_name
profile_url = tw.user.profile_image_url if tw.user.profile_image_url is not None else ""
if not username:
username = "anon."
@@ -416,7 +416,7 @@
get_logger().debug("write http " + repr(project)) #@UndefinedVariable
r = requests.put(content_file_write, data=anyjson.dumps(project), headers={'content-type':'application/json'}, params=post_param);
get_logger().debug("write http " + repr(r) + " content " + r.text) #@UndefinedVariable
- if r.status_code != requests.codes.ok:
+ if r.status_code != requests.codes.ok: # @UndefinedVariable
r.raise_for_status()
else:
if content_file_write and os.path.exists(content_file_write):
--- a/script/utils/get_stats.py Wed Apr 10 18:38:21 2013 +0200
+++ b/script/utils/get_stats.py Fri Jun 07 11:55:46 2013 +0200
@@ -1,14 +1,13 @@
+from lxml import etree
import httplib2
-import anyjson
-from lxml import etree
+import pprint
import sys
-import pprint
def get_stats(url):
h = httplib2.Http()
- resp, content = h.request(url)
+ _, content = h.request(url)
#project = anyjson.deserialize(content)
root = etree.fromstring(content)
--- a/script/utils/merge_tweets.py Wed Apr 10 18:38:21 2013 +0200
+++ b/script/utils/merge_tweets.py Fri Jun 07 11:55:46 2013 +0200
@@ -1,12 +1,15 @@
#from models import setup_database
from iri_tweet.models import setup_database, TweetSource, Tweet, TweetLog
-from iri_tweet.utils import TwitterProcessor, get_oauth_token, show_progress
+from iri_tweet.processor import TwitterProcessorStatus
+from iri_tweet.utils import get_oauth_token, show_progress
+import anyjson
import argparse
-import sys
+import codecs
+import logging
import re
-import anyjson
-import math
-import codecs
+import sys
+
+logger = logging.getLogger(__name__)
def get_option():
@@ -16,6 +19,10 @@
help="log to file", metavar="LOG", default="stderr")
parser.add_argument("-v", dest="verbose", action="count",
help="verbose", default=0)
+ parser.add_option("-k", "--key", dest="consumer_key",
+ help="Twitter consumer key", metavar="CONSUMER_KEY")
+ parser.add_option("-s", "--secret", dest="consumer_secret",
+ help="Twitter consumer secret", metavar="CONSUMER_SECRET")
parser.add_argument("-q", dest="quiet", action="count",
help="quiet", default=0)
parser.add_argument("--query-user", dest="query_user", action="store_true",
@@ -38,7 +45,7 @@
access_token = None
if options.query_user:
- access_token = get_oauth_token(options.token_filename)
+ access_token = get_oauth_token(options.consumer_key, options.consumer_secret, options.token_filename)
#open source
src_conn_str = options.source[0].strip()
@@ -60,7 +67,7 @@
session_src = Session_src()
session_tgt = Session_tgt()
- count_tw_query = Tweet.__table__.count()
+ count_tw_query = Tweet.__table__.count() # @UndefinedVariable
count_tw = engine_src.scalar(count_tw_query)
@@ -83,23 +90,28 @@
tweet_obj = anyjson.deserialize(tweet_source)
if 'text' not in tweet_obj:
- tweet_log = TweetLog(tweet_source_id=source_id, status=TweetLog.TWEET_STATUS['NOT_TWEET'])
+ tweet_log = TweetLog(tweet_source_id=tweet.tweet_source.id, status=TweetLog.TWEET_STATUS['NOT_TWEET'])
session_tgt.add(tweet_log)
else:
- tp = TwitterProcessor(None, tweet_source, None, session_tgt, access_token, options.token_filename, user_query_twitter=options.query_user)
+ tp = TwitterProcessorStatus(None, tweet_source, None, session_tgt, access_token, options.token_filename, user_query_twitter=options.query_user, logger=logger)
tp.process()
session_tgt.flush()
- show_progress(i+1, count_tw, repr(progress_text+tweet.text), 70)
+ ptext = progress_text + tweet.text
+ show_progress(i+1, count_tw, ptext.replace("\n",""), 70)
session_tgt.commit()
print u"%d new tweet added" % (added)
finally:
- session_tgt.close() if session_tgt is not None else None
- session_src.close() if session_src is not None else None
- conn_tgt.close() if conn_tgt is not None else None
- conn_src.close() if conn_src is not None else None
+ if session_tgt is not None:
+ session_tgt.close()
+ if session_src is not None:
+ session_src.close()
+ if conn_tgt is not None:
+ conn_tgt.close()
+ if conn_src is not None:
+ conn_src.close()
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/script/utils/search_topsy.py Fri Jun 07 11:55:46 2013 +0200
@@ -0,0 +1,168 @@
+from blessings import Terminal
+from iri_tweet import models, utils
+from iri_tweet.processor import TwitterProcessorStatus
+from optparse import OptionParser
+import logging
+import math
+import re
+import requests
+import time
+import twitter
+
+logger = logging.getLogger(__name__)
+
+APPLICATION_NAME = "Tweet recorder user"
+CONSUMER_KEY = "Vdr5ZcsjI1G3esTPI8yDg"
+CONSUMER_SECRET = "LMhNrY99R6a7E0YbZZkRFpUZpX5EfB1qATbDk1sIVLs"
+
+
+class TopsyResource(object):
+
+ def __init__(self, query, **kwargs):
+
+ self.options = kwargs
+ self.options['q'] = query
+ self.url = kwargs.get("url", "http://otter.topsy.com/search.json")
+ self.page = 0
+ self.req = None
+ self.res = {}
+
+ def __initialize(self):
+
+ params = {}
+ params.update(self.options)
+ self.req = requests.get(self.url, params=params)
+ self.res = self.req.json
+
+ def __next_page(self):
+ page = self.res.get("response").get("page") + 1
+ params = {}
+ params.update(self.options)
+ params['page'] = page
+ self.req = requests.get(self.url, params=params)
+ self.res = self.req.json
+
+ def __iter__(self):
+ if not self.req:
+ self.__initialize()
+ while "response" in self.res and "list" in self.res.get("response") and self.res.get("response").get("list"):
+ for item in self.res.get("response").get("list"):
+ yield item
+ self.__next_page()
+
+ def total(self):
+ if not self.res:
+ return 0
+ else:
+ return self.res.get("response",{}).get("total",0)
+
+
+
+def get_option():
+
+ parser = OptionParser()
+
+ parser.add_option("-d", "--database", dest="database",
+ help="Input database", metavar="DATABASE")
+ parser.add_option("-Q", dest="query",
+ help="query", metavar="QUERY")
+ parser.add_option("-t", dest="token_filename", metavar="TOKEN_FILENAME", default=".oauth_token",
+ help="Token file name")
+ parser.add_option("-T", dest="topsy_apikey", metavar="TOPSY_APIKEY", default=None,
+ help="Topsy apikey")
+
+ utils.set_logging_options(parser)
+
+ return parser.parse_args()
+
+
+
+if __name__ == "__main__":
+
+ (options, args) = get_option()
+
+ utils.set_logging(options);
+
+
+ acess_token_key, access_token_secret = utils.get_oauth_token(consumer_key=CONSUMER_KEY, consumer_secret=CONSUMER_SECRET, options.token_filename, application_name=APPLICATION_NAME)
+
+ t = twitter.Twitter(domain="api.twitter.com", auth=twitter.OAuth(acess_token_key, access_token_secret, CONSUMER_KEY, CONSUMER_SECRET), secure=True)
+ t.secure = True
+
+ conn_str = options.database.strip()
+ if not re.match("^\w+://.+", conn_str):
+ conn_str = 'sqlite:///' + conn_str
+
+ engine, metadata, Session = models.setup_database(conn_str, echo=((options.verbose-options.quiet)>0), create_all=True)
+ session = None
+
+
+ topsy_parameters = {
+ 'apikey': options.topsy_apikey,
+ 'perpage': 100,
+ 'window': 'a',
+ 'type': 'tweet',
+ 'hidden': True,
+ }
+
+ term = Terminal()
+
+ try:
+ session = Session()
+
+ results = None
+ page = 1
+ print options.query
+
+ tr = TopsyResource(options.query, **topsy_parameters)
+
+ move_up = 0
+
+ for i,item in enumerate(tr):
+ # get id
+ url = item.get("url")
+ tweet_id = url.split("/")[-1]
+
+ if move_up > 0:
+ print((move_up+1)*term.move_up())
+ move_up = 0
+
+ print ("%d/%d:%03d%% - %s - %r" % (i+1, tr.total(), int(float(i+1)/float(tr.total())*100.0), tweet_id, item.get("content") ) + term.clear_eol())
+ move_up += 1
+
+ count_tweet = session.query(models.Tweet).filter_by(id_str=tweet_id).count()
+
+ if count_tweet:
+ continue
+ try:
+ tweet = t.statuses.show(id=tweet_id, include_entities=True)
+ except twitter.api.TwitterHTTPError as e:
+ if e.e.code == 404 or e.e.code == 403:
+ continue
+ else:
+ raise
+
+ processor = TwitterProcessorStatus(tweet, None, None, session, None, options.token_filename, logger)
+ processor.process()
+ session.flush()
+ session.commit()
+
+ time_to_sleep = int(math.ceil((tweet.rate_limit_reset - time.mktime(time.gmtime())) / tweet.rate_limit_remaining))
+
+ print "rate limit remaining %s of %s" % (str(tweet.rate_limit_remaining), str(tweet.headers.getheader('x-ratelimit-limit'))) + term.clear_eol()
+ move_up += 1
+ for i in xrange(time_to_sleep):
+ if i:
+ print(2*term.move_up())
+ else:
+ move_up += 1
+ print(("Sleeping for %d seconds, %d remaining" % (time_to_sleep, time_to_sleep-i)) + term.clear_eol())
+ time.sleep(1)
+
+ except twitter.api.TwitterHTTPError as e:
+ fmt = ("." + e.format) if e.format else ""
+ print "Twitter sent status %s for URL: %s%s using parameters: (%s)\ndetails: %s" % (repr(e.e.code), repr(e.uri), repr(fmt), repr(e.uriparts), repr(e.response_data))
+
+ finally:
+ if session:
+ session.close()
--- a/script/utils/tweet_twitter_user.py Wed Apr 10 18:38:21 2013 +0200
+++ b/script/utils/tweet_twitter_user.py Fri Jun 07 11:55:46 2013 +0200
@@ -38,6 +38,7 @@
parser.add_option("-t", dest="token_filename", metavar="TOKEN_FILENAME", default=".oauth_token",
help="Token file name")
parser.add_option("-S", dest="simulate", metavar="SIMULATE", default=False, action="store_true", help="Simulate call to twitter. Do not change the database")
+ parser.add_option("--direct-message", dest="direct_message", metavar="DIRECT_MESSAGE", default=False, action="store_true", help="send direc t message to the user, else create a status update mentioning the user (@username)")
parser.add_option("-f", dest="force", metavar="FORCE", default=False, action="store_true", help="force sending message to all user even if it has already been sent")
@@ -103,16 +104,25 @@
query_res = query.all()
- acess_token_key, access_token_secret = get_oauth_token(options.token_filename, application_name=APPLICATION_NAME, consumer_key=CONSUMER_KEY, consumer_secret=CONSUMER_SECRET)
+ acess_token_key, access_token_secret = get_oauth_token(consumer_key=CONSUMER_KEY, consumer_secret=CONSUMER_SECRET, token_file_path=options.token_filename, application_name=APPLICATION_NAME)
t = twitter.Twitter(auth=twitter.OAuth(acess_token_key, access_token_secret, CONSUMER_KEY, CONSUMER_SECRET))
for user in query_res:
screen_name = user.screen_name
- message = u"@%s: %s" % (screen_name, base_message)
- get_logger().debug("new status : " + message) #@UndefinedVariable
+ if options.direct_message:
+ message = base_message
+ else:
+ message = u"@%s: %s" % (screen_name, base_message)
+
+ print("new message : " + message)
+ get_logger().debug("new message : " + message) #@UndefinedVariable
+
if not options.simulate:
- t.statuses.update(status=message)
+ if options.direct_message:
+ t.direct_messages.new(user_id=user.id, screen_name=screen_name, text=message)
+ else:
+ t.statuses.update(status=message)
user_message = UserMessage(user_id=user.id, message_id=message_obj.id)
session.add(user_message)
session.flush()
--- a/script/virtualenv/res/credential.txt Wed Apr 10 18:38:21 2013 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,20 +0,0 @@
-Consumer key
-54ThDZhpEjokcMgHJOMnQA
-
-Consumer secret
-wUoL9UL2T87tfc97R0Dff2EaqRzpJ5XGdmaN2XK3udA
-
-access_tokens:
-47312923-LiNTtz0I18YXMVIrFeTuhmH7bOvYsK6p3Ln2Dc
-
-access_secret:
-r3LoXVcjImNAElUpWqTu2SG2xCdWFHkva7xeQoncA
-
-Request token URL
-http://twitter.com/oauth/request_token
-
-Access token URL
-http://twitter.com/oauth/access_token
-
-Authorize URL
-http://twitter.com/oauth/authorize
\ No newline at end of file
--- a/script/virtualenv/res/lib/lib_create_env.py Wed Apr 10 18:38:21 2013 +0200
+++ b/script/virtualenv/res/lib/lib_create_env.py Fri Jun 07 11:55:46 2013 +0200
@@ -16,39 +16,41 @@
URLS = {
#'': {'setup': '', 'url':'', 'local':''},
- 'DISTRIBUTE': {'setup': 'distribute', 'url':'http://pypi.python.org/packages/source/d/distribute/distribute-0.6.32.tar.gz', 'local':"distribute-0.6.32.tar.gz", 'install': {'method': 'pip', 'option_str': None, 'dict_extra_env': None}},
+ 'DISTRIBUTE': {'setup': 'distribute', 'url':'http://pypi.python.org/packages/source/d/distribute/distribute-0.6.34.tar.gz', 'local':"distribute-0.6.34.tar.gz", 'install': {'method': 'pip', 'option_str': None, 'dict_extra_env': None}},
'ANYJSON': {'setup': 'anyjson', 'url':'http://pypi.python.org/packages/source/a/anyjson/anyjson-0.3.3.tar.gz', 'local':"anyjson-0.3.3.tar.gz", 'install': {'method': 'pip', 'option_str': None, 'dict_extra_env': None}},
'OAUTH2': { 'setup': 'python-oauth2', 'url':"https://github.com/simplegeo/python-oauth2/tarball/hudson-python-oauth2-211", 'local':"python-oauth2-1.5-211.tar.gz", 'install': {'method': 'pip', 'option_str': None, 'dict_extra_env': None}},
- 'HTTPLIB2': { 'setup': 'httplib2', 'url':'http://pypi.python.org/packages/source/h/httplib2/httplib2-0.7.7.tar.gz', 'local':"httplib2-0.7.7.tar.gz", 'install': {'method': 'pip', 'option_str': None, 'dict_extra_env': None}},
+ 'HTTPLIB2': { 'setup': 'httplib2', 'url':'http://pypi.python.org/packages/source/h/httplib2/httplib2-0.8.tar.gz', 'local':"httplib2-0.8.tar.gz", 'install': {'method': 'pip', 'option_str': None, 'dict_extra_env': None}},
'LOCKFILE': {'setup': 'lockfile', 'url':'http://code.google.com/p/pylockfile/downloads/detail?name=lockfile-0.9.1.tar.gz', 'local':"lockfile-0.9.1.tar.gz", 'install': {'method': 'pip', 'option_str': None, 'dict_extra_env': None}},
'DAEMON': {'setup': 'python-daemon', 'url':'http://pypi.python.org/packages/source/p/python-daemon/python-daemon-1.5.5.tar.gz', 'local':"python-daemon-1.5.5.tar.gz", 'install': {'method': 'pip', 'option_str': None, 'dict_extra_env': None}},
'DATEUTIL': {'setup': 'python-dateutil', 'url':'http://pypi.python.org/packages/source/p/python-dateutil/python-dateutil-2.1.tar.gz', 'local':"python-dateutil-2.1.tar.gz", 'install': {'method': 'pip', 'option_str': None, 'dict_extra_env': None}},
- 'PYTZ': {'setup': 'pytz', 'url':'http://pypi.python.org/packages/source/p/pytz/pytz-2012h.tar.bz2', 'local':"pytz-2012h.tar.bz2", 'install': {'method': 'pip', 'option_str': None, 'dict_extra_env': None}},
- 'SIMPLEJSON': {'setup': 'simplejson', 'url':'http://pypi.python.org/packages/source/s/simplejson/simplejson-2.6.2.tar.gz', 'local':"simplejson-2.6.2.tar.gz", 'install': {'method': 'pip', 'option_str': None, 'dict_extra_env': None}},
- 'SQLALCHEMY': {'setup': 'sqlalchemy', 'url':'http://downloads.sourceforge.net/project/sqlalchemy/sqlalchemy/0.8.0b1/SQLAlchemy-0.8.0b1.tar.gz?r=http%3A%2F%2Fwww.sqlalchemy.org%2Fdownload.html&ts=1355091775&use_mirror=ignum', 'local':"SQLAlchemy-0.8.0b1.tar.gz", 'install': {'method': 'pip', 'option_str': None, 'dict_extra_env': None}},
- 'TWEEPY': {'setup': 'tweepy', 'url':'https://github.com/tweepy/tweepy/archive/1.12.tar.gz', 'local':"tweepy-1.12.tar.gz", 'install': {'method': 'pip', 'option_str': None, 'dict_extra_env': None}},
- 'TWITTER': {'setup': 'twitter', 'url':'http://pypi.python.org/packages/source/t/twitter/twitter-1.9.0.tar.gz', 'local':"twitter-1.9.0.tar.gz", 'install': {'method': 'pip', 'option_str': None, 'dict_extra_env': None}},
+ 'PYTZ': {'setup': 'pytz', 'url':'http://pypi.python.org/packages/source/p/pytz/pytz-2013b.tar.bz2', 'local':"pytz-2013b.tar.bz2", 'install': {'method': 'pip', 'option_str': None, 'dict_extra_env': None}},
+ 'SIMPLEJSON': {'setup': 'simplejson', 'url':'http://pypi.python.org/packages/source/s/simplejson/simplejson-3.1.3.tar.gz', 'local':"simplejson-3.1.3.tar.gz", 'install': {'method': 'pip', 'option_str': None, 'dict_extra_env': None}},
+ 'SQLALCHEMY': {'setup': 'sqlalchemy', 'url':'http://www.python.org/pypi/SQLAlchemy/0.8.1', 'local':"SQLAlchemy-0.8.1.tar.gz", 'install': {'method': 'pip', 'option_str': None, 'dict_extra_env': None}},
+ 'TWITTER': {'setup': 'twitter', 'url':'http://pypi.python.org/packages/source/t/twitter/twitter-1.9.2.tar.gz', 'local':"twitter-1.9.2.tar.gz", 'install': {'method': 'pip', 'option_str': None, 'dict_extra_env': None}},
'TWITTER-TEXT': {'setup': 'twitter-text', 'url':'https://github.com/dryan/twitter-text-py/archive/master.tar.gz', 'local':"twitter-text-1.0.4.tar.gz", 'install': {'method': 'pip', 'option_str': None, 'dict_extra_env': None}},
- 'REQUESTS': {'setup': 'requests', 'url':'https://github.com/kennethreitz/requests/archive/v1.1.0.tar.gz', 'local':'requests-v1.1.0.tar.gz', 'install' : {'method':'pip', 'option_str': None, 'dict_extra_env': None}},
+ 'REQUESTS': {'setup': 'requests', 'url':'https://github.com/kennethreitz/requests/archive/v1.2.0.tar.gz', 'local':'requests-v1.2.0.tar.gz', 'install' : {'method':'pip', 'option_str': None, 'dict_extra_env': None}},
+ 'OAUTHLIB': {'setup': 'oauthlib', 'url':'https://github.com/idan/oauthlib/archive/0.4.0.tar.gz', 'local':'oauthlib-0.4.0.tar.gz', 'install' : {'method':'pip', 'option_str': None, 'dict_extra_env': None}},
+ 'REQUESTS-OAUTHLIB': {'setup': 'requests-oauthlib', 'url':'https://github.com/requests/requests-oauthlib/archive/master.tar.gz', 'local':'requests-oauthlib-0.3.0.tar.gz', 'install' : {'method':'pip', 'option_str': None, 'dict_extra_env': None}},
+ 'BLESSINGS': {'setup': 'blessings', 'url':'https://github.com/erikrose/blessings/archive/1.5.tar.gz', 'local':'blessings-1.5.tar.gz', 'install' : {'method':'pip', 'option_str': None, 'dict_extra_env': None}}
}
if system_str == 'Windows':
URLS.update({
- 'PSYCOPG2': {'setup': 'psycopg2','url': 'psycopg2-2.4.5.win32-py2.7-pg9.1.3-release.zip', 'local':"psycopg2-2.4.5.win32-py2.7-pg9.1.3-release.zip", 'install': {'method': 'install_psycopg2', 'option_str': None, 'dict_extra_env': None}},
+ 'PSYCOPG2': {'setup': 'psycopg2','url': 'psycopg2-2.5.win32-py2.7-pg9.2.4-release.zip', 'local':"psycopg2-2.5.win32-py2.7-pg9.2.4-release.zip", 'install': {'method': 'install_psycopg2', 'option_str': None, 'dict_extra_env': None}},
'LXML': {'setup': 'lxml', 'url': 'http://pypi.python.org/packages/2.7/l/lxml/lxml-2.3-py2.7-win32.egg', 'local':"lxml-2.3-py2.7-win32.egg", 'install': {'method': 'easy_install', 'option_str': None, 'dict_extra_env': None}},
})
else:
if system_str == "Darwin":
- lxml_options = {'STATIC_DEPS': 'true', 'LIBXML2_VERSION': '2.8.0', 'LIBXSLT_VERSION': '1.1.28', 'LIBICONV_VERSION': '1.14'}
+ lxml_options = {'STATIC_DEPS': 'true', 'LIBXML2_VERSION': '2.9.1', 'LIBXSLT_VERSION': '1.1.28', 'LIBICONV_VERSION': '1.14'}
lxml_method = 'pip'
else:
lxml_options = None
lxml_method = 'pip'
URLS.update({
- 'PSYCOPG2': {'setup': 'psycopg2','url': 'http://www.psycopg.org/psycopg/tarballs/PSYCOPG-2-4/psycopg2-2.4.5.tar.gz', 'local':"psycopg2-2.4.5.tar.gz", 'install': {'method': 'pip', 'option_str': None, 'dict_extra_env': None}},
- 'LXML': {'setup': 'lxml', 'url':"lxml-3.0.1.tar.gz", 'local':"lxml-3.0.1.tar.gz", 'install': {'method': lxml_method, 'option_str': None, 'dict_extra_env': lxml_options}},
+ 'PSYCOPG2': {'setup': 'psycopg2','url': 'http://www.psycopg.org/psycopg/tarballs/PSYCOPG-2-5/psycopg2-2.5.tar.gz', 'local':"psycopg2-2.5.tar.gz", 'install': {'method': 'pip', 'option_str': None, 'dict_extra_env': None}},
+ 'LXML': {'setup': 'lxml', 'url':"lxml-3.2.1.tar.bz2", 'local':"lxml-3.2.1.tar.bz2", 'install': {'method': lxml_method, 'option_str': None, 'dict_extra_env': lxml_options}},
})
Binary file script/virtualenv/res/src/SQLAlchemy-0.8.0b1.tar.gz has changed
Binary file script/virtualenv/res/src/SQLAlchemy-0.8.1.tar.gz has changed
Binary file script/virtualenv/res/src/blessings-1.5.tar.gz has changed
Binary file script/virtualenv/res/src/distribute-0.6.32.tar.gz has changed
Binary file script/virtualenv/res/src/distribute-0.6.34.tar.gz has changed
Binary file script/virtualenv/res/src/httplib2-0.7.7.tar.gz has changed
Binary file script/virtualenv/res/src/httplib2-0.8.tar.gz has changed
Binary file script/virtualenv/res/src/lxml-3.0.1.tar.gz has changed
Binary file script/virtualenv/res/src/lxml-3.2.1.tar.bz2 has changed
Binary file script/virtualenv/res/src/oauthlib-0.4.0.tar.gz has changed
Binary file script/virtualenv/res/src/psycopg2-2.4.5.tar.gz has changed
Binary file script/virtualenv/res/src/psycopg2-2.5.tar.gz has changed
Binary file script/virtualenv/res/src/psycopg2-2.5.win32-py2.7-pg9.2.4-release.zip has changed
Binary file script/virtualenv/res/src/pytz-2012h.tar.bz2 has changed
Binary file script/virtualenv/res/src/pytz-2013b.tar.bz2 has changed
Binary file script/virtualenv/res/src/requests-1.1.0.tar.gz has changed
Binary file script/virtualenv/res/src/requests-oauthlib-0.3.0.tar.gz has changed
Binary file script/virtualenv/res/src/requests-v1.2.0.tar.gz has changed
Binary file script/virtualenv/res/src/simplejson-2.6.2.tar.gz has changed
Binary file script/virtualenv/res/src/simplejson-3.1.3.tar.gz has changed
Binary file script/virtualenv/res/src/tweepy-1.12.tar.gz has changed
Binary file script/virtualenv/res/src/twitter-1.9.0.tar.gz has changed
Binary file script/virtualenv/res/src/twitter-1.9.2.tar.gz has changed
--- a/script/virtualenv/script/res/res_create_env.py Wed Apr 10 18:38:21 2013 +0200
+++ b/script/virtualenv/script/res/res_create_env.py Fri Jun 07 11:55:46 2013 +0200
@@ -17,10 +17,12 @@
'DATEUTIL',
'SIMPLEJSON',
'SQLALCHEMY',
- 'TWEEPY',
'TWITTER',
'TWITTER-TEXT',
- 'REQUESTS',
+ 'REQUESTS',
+ 'OAUTHLIB',
+ 'REQUESTS-OAUTHLIB',
+ 'BLESSINGS'
]
if system_str == "Linux":
--- a/script/virtualenv/script/virtualenv.py Wed Apr 10 18:38:21 2013 +0200
+++ b/script/virtualenv/script/virtualenv.py Fri Jun 07 11:55:46 2013 +0200
@@ -4,7 +4,7 @@
# If you change the version here, change it in setup.py
# and docs/conf.py as well.
-__version__ = "1.8.4" # following best practices
+__version__ = "1.9.1" # following best practices
virtualenv_version = __version__ # legacy, again
import base64
@@ -862,6 +862,19 @@
'VIRTUALENV_DISTRIBUTE to make it the default ')
parser.add_option(
+ '--no-setuptools',
+ dest='no_setuptools',
+ action='store_true',
+ help='Do not install distribute/setuptools (or pip) '
+ 'in the new virtualenv.')
+
+ parser.add_option(
+ '--no-pip',
+ dest='no_pip',
+ action='store_true',
+ help='Do not install pip in the new virtualenv.')
+
+ parser.add_option(
'--setuptools',
dest='use_distribute',
action='store_false',
@@ -961,7 +974,9 @@
use_distribute=options.use_distribute,
prompt=options.prompt,
search_dirs=options.search_dirs,
- never_download=options.never_download)
+ never_download=options.never_download,
+ no_setuptools=options.no_setuptools,
+ no_pip=options.no_pip)
if 'after_install' in globals():
after_install(options, home_dir)
@@ -1048,7 +1063,8 @@
def create_environment(home_dir, site_packages=False, clear=False,
unzip_setuptools=False, use_distribute=False,
- prompt=None, search_dirs=None, never_download=False):
+ prompt=None, search_dirs=None, never_download=False,
+ no_setuptools=False, no_pip=False):
"""
Creates a new environment in ``home_dir``.
@@ -1066,14 +1082,16 @@
install_distutils(home_dir)
- if use_distribute:
- install_distribute(py_executable, unzip=unzip_setuptools,
- search_dirs=search_dirs, never_download=never_download)
- else:
- install_setuptools(py_executable, unzip=unzip_setuptools,
- search_dirs=search_dirs, never_download=never_download)
+ if not no_setuptools:
+ if use_distribute:
+ install_distribute(py_executable, unzip=unzip_setuptools,
+ search_dirs=search_dirs, never_download=never_download)
+ else:
+ install_setuptools(py_executable, unzip=unzip_setuptools,
+ search_dirs=search_dirs, never_download=never_download)
- install_pip(py_executable, search_dirs=search_dirs, never_download=never_download)
+ if not no_pip:
+ install_pip(py_executable, search_dirs=search_dirs, never_download=never_download)
install_activate(home_dir, bin_dir, prompt)
@@ -1189,8 +1207,9 @@
else:
if f is not None:
f.close()
- # special-case custom readline.so on OS X:
- if modname == 'readline' and sys.platform == 'darwin' and not filename.endswith(join('lib-dynload', 'readline.so')):
+ # special-case custom readline.so on OS X, but not for pypy:
+ if modname == 'readline' and sys.platform == 'darwin' and not (
+ is_pypy or filename.endswith(join('lib-dynload', 'readline.so'))):
dst_filename = join(dst_prefix, 'lib', 'python%s' % sys.version[:3], 'readline.so')
else:
dst_filename = change_prefix(filename, dst_prefix)
@@ -1351,11 +1370,6 @@
if sys.executable != py_executable:
## FIXME: could I just hard link?
executable = sys.executable
- if is_cygwin and os.path.exists(executable + '.exe'):
- # Cygwin misreports sys.executable sometimes
- executable += '.exe'
- py_executable += '.exe'
- logger.info('Executable actually exists in %s' % executable)
shutil.copyfile(executable, py_executable)
make_exe(py_executable)
if is_win or is_cygwin:
@@ -1901,143 +1915,146 @@
##file site.py
SITE_PY = convert("""
-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
-CP4PNIke9w==
+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
""")
##file ez_setup.py
@@ -2121,93 +2138,93 @@
##file distribute_setup.py
DISTRIBUTE_SETUP_PY = convert("""
-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
+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
""")
##file activate.sh
@@ -2273,9 +2290,9 @@
##file deactivate.bat
DEACTIVATE_BAT = convert("""
-eJxzSE3OyFfIT0vj4spMU0hJTcvMS01RiPf3cYkP8wwKCXX0iQ8I8vcNCFHQ4FIAguLUEgUliIit
-KhZlqkpcnCA1WKRsuTTxWBIZ4uHv5+Hv64piEVwU3TK4BNBCmHIcKvDb6xjigWIjkI9uF1AIu7dA
-akGGW7n6uXABALCXXUI=
+eJxzSE3OyFfIT0vj4ipOLVEI8wwKCXX0iXf1C7Pl4spMU0hJTcvMS01RiPf3cYmHyQYE+fsGhCho
+cCkAAUibEkTEVhWLMlUlLk6QGixStlyaeCyJDPHw9/Pw93VFsQguim4ZXAJoIUw5DhX47XUM8UCx
+EchHtwsohN1bILUgw61c/Vy4AJYPYm4=
""")
##file activate.ps1
Binary file script/virtualenv/script/virtualenv_support/distribute-0.6.31.tar.gz has changed
Binary file script/virtualenv/script/virtualenv_support/distribute-0.6.34.tar.gz has changed
Binary file script/virtualenv/script/virtualenv_support/pip-1.2.1.tar.gz has changed
Binary file script/virtualenv/script/virtualenv_support/pip-1.3.1.tar.gz has changed
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/web/bpi-floptechno/config.php Fri Jun 07 11:55:46 2013 +0200
@@ -0,0 +1,54 @@
+<?php
+$config = array(
+ 'hashtag' => '#floptechno',
+
+ 'title' => "Les flops technologiques : échouer pour réussir ?",
+
+ 'abstract' => "Cycle Cultures numériques :<br /><b>Les flops technologiques : échouer pour réussir ?</b><br/>Vendredi 31 mai 2013, 19h00, entrée libre<br/>Centre Pompidou, Petite Salle",
+
+ 'description'=> "
+ <p>Les technologies de l’information (TIC) ont connu de nombreux revers et des flops parfois spectaculaires. Comment la réussite peut-elle succéder à des échecs multiples ? Seuls 20% des projets de recherche et développement deviennent des succès commerciaux. Les autres tombent alors dans le « fossé de la désillusion », le gouffre où le cycle de vie d’un produit ne rencontre pas les usagers. Dans quelle temporalité faut-il se placer pour réussir à transformer l’essai ? Comment la compréhension de l’innovation par l’usager est-elle devenue un élément central à la réussite?</p>
+ <p>En questionnant les discours marketing et les fantasmes technophiles ou technophobes, Nicolas Nova met en évidence des principes fondamentaux pour la création de nouveaux produits ou services.</p>
+<ul>
+ <li>
+ <p>Avec <b>Nicolas Nova</b>, chercheur à la HEAD (Genève) et à l’ENSCI (Paris).</p>
+ </li>
+ <li>
+ <p>Animé par <b>Hubert Guillaud</b>, rédacteur en chef d'<a href='http://www.internetactu.net/' target='_blank'>InternetActu.net</a></p>
+ </li>
+</ul>
+ <p>Programme complet sur <a href='http://www.bpi.fr/fr/agenda/conferences_et_debats/cultures_numeriques/les_flops_technologiques_echouer_pour_reussir.html' target='_blank'>bpi.fr</a></p>
+ ",
+
+ 'link' => 'http://www.bpi.fr/fr/agenda/conferences_et_debats/cultures_numeriques/les_flops_technologiques_echouer_pour_reussir.html',
+
+ 'islive' => true,
+
+ 'flv_provider' => 'http',
+ 'flv_file' => 'http://flash.live.tv-radio.com/centre_pompidou/all/pompidou-h264-1.flv',
+ // 'flv_file' => 'http://flash.live.tv-radio.com/publicsenat/all/publicsenat-h264.flv',
+ 'flv_streamer' => '',
+
+ 'keywords' => 'bpi, bibliothèque centre pompidou, TIC, flops, recherche, Nicolas Nova',
+
+ 'rep' => basename(__DIR__),
+
+ 'partenaires'=> "<a href='http://www.bpi.fr/' class='footerLink' target='_blank'>Bibliothèque Publique d'Information/a>
+ | <a href='http://www.chroniquesdelarentreelitteraire.com' class='footerLink' target='_blank'>Les chroniques de la rentrée littéraire</a>
+ | <a href='http://www.internetactu.net/' class='footerLink' target='_blank'>InternetActu.net</a>
+ | <a href='http://www.iri.centrepompidou.fr/' class='footerLink' target='_blank'>Institut de Recherche et d'Innovation</a>",
+
+ 'client_visual' => 'images/client_visual.jpg',// 480 × 320 pixels
+
+ 'head_logo' => 'images/logo_head.png', // 171 × 63 pixels
+
+ 'slide_background' => 'images/slide_background.jpg', // 606 × 282 pixels
+
+ 'archive_img' => 'images/archive_img.jpg', // 270 × 150 pixels
+
+ 'archive_title' => "Les flops technologiques : échouer pour réussir ?",
+ 'archive_description' => 'par la <a href="http://www.bpi.fr/" target="_blank">Bibliothèque Publique d\'Information</a><br/>au Centre Pompidou le 31/05/2013 19h00',
+
+ // After the event
+ 'metadata' => "43b1188a-6a06-11e2-baaf-00145ea4a2be"
+);
\ No newline at end of file
Binary file web/bpi-floptechno/images/archive_img.jpg has changed
Binary file web/bpi-floptechno/images/client_visual.jpg has changed
Binary file web/bpi-floptechno/images/logo_head.png has changed
Binary file web/bpi-floptechno/images/slide_background.jpg has changed
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/web/bpi-floptechno/index.php Fri Jun 07 11:55:46 2013 +0200
@@ -0,0 +1,6 @@
+<?php
+// Permanent redirection
+header("HTTP/1.1 301 Moved Permanently");
+header("Location: client.php");
+exit();
+?>
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/web/cineconf/config.php Fri Jun 07 11:55:46 2013 +0200
@@ -0,0 +1,4 @@
+<?php
+$config = array(
+ 'rep' => basename(__DIR__),
+);
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/web/cineconf/index.php Fri Jun 07 11:55:46 2013 +0200
@@ -0,0 +1,91 @@
+<?php
+
+$config = array("rep" => basename(__DIR__));
+
+include_once '../common.php';
+
+/**
+ * Do we already have a valid Access Token or need to go get one?
+ */
+
+if (!isset($_SESSION['TWITTER_ACCESS_TOKEN'])) {
+
+ $_SESSION['TWITTER_REDIRECT_URL'] = URL_ROOT . basename(__DIR__) . '/' . basename(__FILE__);
+ // Permanent redirection
+ header("HTTP/1.1 301 Moved Permanently");
+ header("Location: client.php?CONNECT=true&rep=" . basename(__DIR__));
+ exit();
+
+}
+?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
+ "http://www.w3.org/TR/html4/strict.dtd">
+
+<html lang="fr">
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+ <title>Avant-Première Hanna Arendt : Tweet Wall</title>
+ <meta http-equiv="X-UA-Compatible" content="IE=9" />
+
+ <!-- CSS -->
+ <link rel="stylesheet" href="<?php echo(registry_url('tweetcast', 'css')); ?>" type="text/css" media="screen, projection"/>
+ <link rel="stylesheet" href="style.css" type="text/css" media="screen, projection"/>
+
+ <!-- JAVASCRIPT -->
+ <script type="text/javascript" src="<?php echo(registry_url('jquery','js'))?>"></script>
+ <script type="text/javascript" src="<?php echo(registry_url('jquery-mousewheel','js'))?>"></script>
+ <script type="text/javascript" src="<?php echo(registry_url('jquery-scrollto','js'))?>"></script>
+ <script type="text/javascript" src="<?php echo(registry_url('underscore','js'))?>"></script>
+ <script type="text/javascript" src="<?php echo(registry_url('raphael','js'))?>"></script>
+
+ <script type="text/javascript">
+ var favUser='<?php
+ if (isset($_GET['favuser'])) {
+ echo $_GET['favuser'];
+ } else {
+ echo 'AVPHannahArendt';
+ }
+ ?>';
+ </script>
+
+ <script type="text/javascript" src="script.js"></script>
+
+ </head>
+ <body>
+ <div id="main">
+ <div id="visionplayer_1101"></div>
+
+ <script type="text/javascript" src="http://embeddedplayer.visionip.tv/embed/1101?w=836&h=440" ></script>
+
+ <div id="vizcontainer">
+
+ <div class="barre">
+ <form id="recherche">
+ <input autocomplete="off" class="greyed" id="inp_q" value="Rechercher" />
+ <input id="inp_submit" type="submit" />
+ <input id="inp_reset" type="reset" />
+ <div id="time_controls">
+ <div id="time_legende"></div>
+ <div id="time_scale"></div>
+ <a href="#" id="time_zoomout"></a>
+ <a href="#" id="time_zoomin"></a>
+ </div>
+ <div id="recherche_annot">
+ Rechercher par polémique : <span id="rech_list_annot"></span>
+ <br />
+ </div>
+ </form>
+ </div>
+
+ <div id="tweetviz">
+ <ul id="tweetlist"></ul>
+ <div id="timeline"></div>
+ <div id="scrollcont">
+ <div id="scrollin"></div>
+ </div>
+ </div>
+ </div>
+
+ </div>
+ </body>
+</html>
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/web/cineconf/paris.php Fri Jun 07 11:55:46 2013 +0200
@@ -0,0 +1,88 @@
+<?php
+
+$config = array("rep" => basename(__DIR__));
+
+include_once '../common.php';
+
+/**
+ * Do we already have a valid Access Token or need to go get one?
+ */
+
+if (!isset($_SESSION['TWITTER_ACCESS_TOKEN']) ) {
+
+ $_SESSION['TWITTER_REDIRECT_URL'] = URL_ROOT . basename(__DIR__) . '/' . basename(__FILE__);
+ // Permanent redirection
+ header("HTTP/1.1 301 Moved Permanently");
+ header("Location: client.php?CONNECT=true&rep=".basename(__DIR__));
+ exit();
+
+}
+
+?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
+ "http://www.w3.org/TR/html4/strict.dtd">
+
+<html lang="fr">
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+ <title>Avant-Première Hanna Arendt : Tweet Wall</title>
+ <meta http-equiv="X-UA-Compatible" content="IE=9" />
+
+ <!-- CSS -->
+ <link rel="stylesheet" href="<?php echo(registry_url('tweetcast','css'));?>" type="text/css" media="screen, projection"/>
+ <link rel="stylesheet" href="style.css" type="text/css" media="screen, projection"/>
+
+ <!-- JAVASCRIPT -->
+ <script type="text/javascript" src="<?php echo(registry_url('jquery','js'))?>"></script>
+ <script type="text/javascript" src="<?php echo(registry_url('jquery-mousewheel','js'))?>"></script>
+ <script type="text/javascript" src="<?php echo(registry_url('jquery-scrollto','js'))?>"></script>
+ <script type="text/javascript" src="<?php echo(registry_url('underscore','js'))?>"></script>
+ <script type="text/javascript" src="<?php echo(registry_url('raphael','js'))?>"></script>
+
+ <script type="text/javascript">
+ <?php if (isset($_GET['favuser'])) {
+ echo 'var favUser="'.$_GET['favuser'].'";';
+ }
+ ?>
+ </script>
+
+ <script type="text/javascript" src="script.js"></script>
+
+ </head>
+ <body>
+ <div id="main">
+ <div id="visionplayer_1101">
+ <!--<img src="carton.png" />-->
+ </div>
+ <div id="vizcontainer">
+
+ <div class="barre">
+ <form id="recherche">
+ <input autocomplete="off" class="greyed" id="inp_q" value="Rechercher" />
+ <input id="inp_submit" type="submit" />
+ <input id="inp_reset" type="reset" />
+ <div id="time_controls">
+ <div id="time_legende"></div>
+ <div id="time_scale"></div>
+ <a href="#" id="time_zoomout"></a>
+ <a href="#" id="time_zoomin"></a>
+ </div>
+ <div id="recherche_annot">
+ Rechercher par polémique : <span id="rech_list_annot"></span>
+ <br />
+ </div>
+ </form>
+ </div>
+
+ <div id="tweetviz">
+ <ul id="tweetlist"></ul>
+ <div id="timeline"></div>
+ <div id="scrollcont">
+ <div id="scrollin"></div>
+ </div>
+ </div>
+ </div>
+
+ </div>
+ </body>
+</html>
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/web/cineconf/script.js Fri Jun 07 11:55:46 2013 +0200
@@ -0,0 +1,1184 @@
+var tracking_keywords = [ "#AVPHannaArendt" ];
+
+function rejectUser(username) {
+ return (/^[A-Z][a-z]{2,8}[0-9]{4,6}$/.test(username))
+}
+
+if (typeof annotations == "undefined" || !annotations) {
+ var annotations = {
+ "default" : {
+ "colors" : {
+ "h" : 0,
+ "s" : 0
+ }
+ },
+ "positive" : {
+ "display_name" : "++ | accord",
+ "keywords" : [ /\+\+/ ],
+ "colors" : {
+ "h" : .3,
+ "s" : .65
+ }
+ },
+ "negative" : {
+ "display_name" : "-- | désaccord",
+ "keywords" : [ /\-\-/ ],
+ "colors" : {
+ "h" : 0,
+ "s" : .8
+ }
+ },
+ "reference" : {
+ "display_name" : "== | référence",
+ "keywords" : [ /\=\=/ ],
+ "colors" : {
+ "h" : .16,
+ "s" : .8
+ }
+ },
+ "question" : {
+ "display_name" : "?? | question",
+ "keywords" : [ /\?\?/ ],
+ "colors" : {
+ "h" : .6,
+ "s" : .8
+ }
+ }
+ }
+}
+
+if (typeof l10n == "undefined") {
+ l10n = { "rechercher" : "Rechercher" }
+}
+
+if (typeof suggested_keywords == "undefined") {
+ suggested_keywords = [ ];
+}
+
+if (typeof max_pages == "undefined" || !max_pages) {
+ max_pages = 2;
+}
+
+tracking_keywords = _(tracking_keywords).map(function(_w) {
+ return _w.toLowerCase();
+});
+
+var twCx = {
+ tlPaper : null,
+ followLast : true,
+ position : "0",
+ date_levels : [
+ 3600 * 1000,
+ 15 * 60 * 1000,
+ 5 * 60 * 1000,
+ 60 * 1000
+ ],
+ timeLevel : 1,
+ deltaX : 40,
+ tlWidth : 150,
+ tlHeight : 480,
+ globalWords : {},
+ suggestCount : _(suggested_keywords).map(function(_w) {
+ return {
+ "word" : _w,
+ "rgxp" : new RegExp(_w.replace(/(\W)/g, '\\$1'), "im"),
+ "freq" : 0,
+ "annotations" : {}
+ }
+ }),
+ refMouse : { x : 0, y : 0},
+ refPosTl : { x : 0, y : 0},
+ tlMouseMoved : false,
+ tlMouseClicked : false,
+ filtre : null,
+ tlBuffer : '',
+ relHover : [],
+ wheelDelta : 0,
+ scrollEnabled : false,
+ scrollExtent : 8000 - 480,
+ lastScrollPos : 0,
+ urlRegExp : /https?:\/\/[0-9a-zA-Z\.%\/-_]+/g,
+ wordRegExp : /[^ \.&;,'"!\?\d\(\)\+\[\]\\\…\-«»:\/]{3,}/g,
+ stopWords : [
+ 'aussi', 'and', 'avec', 'aux', 'bien', 'car', 'cette', 'comme', 'dans', 'donc', 'des', 'elle', 'encore', 'est',
+ 'être', 'eux', 'faire', 'fait', 'http', 'ici', 'ils', 'les', 'leur', 'leurs', 'mais', 'mes', 'même', 'mon', 'notre',
+ 'non', 'nos', 'nous', 'ont', 'oui', 'par', 'pas', 'peu', 'peut', 'plus', 'pour', 'que', 'qui', 'ses' ,'son', 'sont', 'sur',
+ 'tes', 'très', 'the', 'ton', 'tous', 'tout', 'une', 'votre', 'vos', 'vous'
+ ]
+ }
+
+function getTweets(options) {
+
+ function getTweetUrl(url) {
+ $.getJSON(url, function(data) {
+ options.tweets = options.tweets.concat(data);
+ options.currentPage++;
+ if (options.cbData) {
+ options.cbData();
+ }
+ var _isLast = true;
+ if (data.results && data.results.length) {
+ var _oldestTweetId = data.results[data.results.length - 1].id_str,
+ _maxId = _oldestTweetId;
+ if (options.currentPage < options.pages) {
+ _isLast = false;
+ getTweetUrl(baseurl + firstparams + '&max_id=' + _maxId + lastparams);
+ }
+ }
+
+ if (_isLast) {
+ options.tweets.sort(function(a,b) {
+ return a.id - b.id;
+ });
+ if (options.cbEnd) {
+ options.cbEnd();
+ }
+ }
+ });
+ }
+
+ options.tweets = [];
+ options.pages || (options.pages = 1);
+ options.rpp || (options.rpp = 100);
+ options.currentPage = 0;
+ var baseurl = "search_tweets.php",
+ firstparams = "?endpoint=favorites/list" + (typeof favUser === "string" ? ("&screen_name=" + encodeURIComponent(favUser)) : "")
+ + "&count=" + options.rpp + "&include_entities=true",
+ lastparams = (options.since_id ? "&since_id=" + options.since_id : '' ) + "&callback=?",
+ jsonurl = baseurl + firstparams + lastparams;
+ getTweetUrl(jsonurl);
+}
+
+function getColor(annotation, lum) {
+ return Raphael.hsl2rgb(annotations[annotation].colors.h, annotations[annotation].colors.s, lum);
+}
+
+function tweetPopup(url) {
+ var popW = 550,
+ popH = 350,
+ scrW = screen.width,
+ scrH = screen.height,
+ posX = Math.round((scrW/2)-(popW/2)),
+ posY = (scrH > popH ? Math.round((scrH/2)-(popH/2)) : 0);
+ window.open(url,
+ '',
+ 'left=' + posX + ',top=' + posY + ',width=' + popW + ',height=' + popH + ',personalbar=0,toolbar=0,scrollbars=1,resizable=1');
+}
+
+function arc(source, target) {
+ var x3 = .3 * target.y - .3 * source.y + .8 * source.x + .2 * target.x;
+ var y3 = .8 * source.y + .2 * target.y - .3 * target.x + .3 * source.x;
+ var x4 = .3 * target.y - .3 * source.y + .2 * source.x + .8 * target.x;
+ var y4 = .2 * source.y + .8 * target.y - .3 * target.x + .3 * source.x;
+ return "M" + source.x + " " + source.y + "C" + [x3, y3, x4, y4, target.x, target.y].join(" ");
+}
+
+function addTweet(tweet) {
+ if (!tweet) {
+ console.log(tweet);
+ return;
+ }
+
+ if (rejectUser(tweet.from_user)) {
+ return;
+ }
+
+ function backRef(source_id, target_id, type) {
+ var target = tweetById(target_id);
+ if (target) {
+ var brobj = {
+ "referenced_by_id" : source_id,
+ "type" : type
+ }
+ if (target.backRefs) {
+ target.backRefs.push(brobj);
+ } else {
+ target.backRefs = [ brobj ]
+ }
+ }
+ }
+
+ _(['id', 'from_user_id', 'in_reply_to_status_id']).each(function(_i) {
+ tweet[_i] = tweet[_i + '_str'];
+ delete tweet[_i + '_str'];
+ });
+
+ if (_(twCx.idIndex).indexOf(tweet.id) != -1) {
+ return;
+ }
+
+ tweet.html_parts = []
+
+ if (tweet.entities && tweet.entities.user_mentions) {
+ for (var _i = 0; _i < tweet.entities.user_mentions.length; _i++) {
+ var _m = tweet.entities.user_mentions[_i];
+ tweet.html_parts.push({
+ "text" : "@" + _m.screen_name,
+ "start" : _m.indices[0],
+ "end" : _m.indices[1],
+ "link" :'<a href="http://twitter.com/' + _m.screen_name + '" onclick="filtrerTexte(\'' + _m.screen_name + '\'); return false;" target="_blank">'
+ });
+ }
+ }
+
+ if (tweet.entities && tweet.entities.hashtags) {
+ for (var _i = 0; _i < tweet.entities.hashtags.length; _i++) {
+ var _m = tweet.entities.hashtags[_i],
+ _h = "#" + _m.text;
+ tweet.html_parts.push({
+ "text" : _h,
+ "start" : _m.indices[0],
+ "end" : _m.indices[1],
+ "link" :'<a href="http://twitter.com/search?q=' + encodeURIComponent(_h) + '" onclick="filtrerTexte(\'' + _.escape(_h) + '\'); return false;" target="_blank">'
+ });
+ }
+ }
+
+ if (tweet.entities && tweet.entities.urls) {
+ for (var _i = 0; _i < tweet.entities.urls.length; _i++) {
+ var _m = tweet.entities.urls[_i];
+ tweet.html_parts.push({
+ "text" : _m.display_url || _m.url,
+ "start" : _m.indices[0],
+ "end" : _m.indices[1],
+ "link" :'<a href="' + _m.url + '" target="_blank">'
+ });
+ }
+ }
+ tweet.date_value = Date.parse(tweet.created_at.replace(/(\+|-)/,'UTC$1'));
+
+ var ann = [];
+ for (var j in annotations) {
+ if (j != "default") {
+ for (var k in annotations[j].keywords) {
+ if (tweet.text.search(annotations[j].keywords[k]) != -1) {
+ ann.push(j);
+ break;
+ }
+ }
+ }
+ }
+ tweet.annotations = ann;
+
+ if (tweet.in_reply_to_status_id) {
+ backRef( tweet.id, tweet.in_reply_to_status_id, "reply" );
+ }
+
+ if (tweet.retweeted_status && tweet.retweeted_status.id_str) {
+ tweet.retweeted_status_id = tweet.retweeted_status.id_str;
+ backRef( tweet.id, tweet.retweeted_status_id, "retweet" );
+ }
+
+
+ var tab = tweet.text.replace(twCx.urlRegExp,'').match(twCx.wordRegExp);
+ _(tab).each(function(w) {
+ var word = w.toLowerCase();
+ if (_(twCx.stopWords).indexOf(word) == -1 && _(tracking_keywords).indexOf(word) == -1 && word[0] != '@') {
+ if (twCx.globalWords[word]) {
+ twCx.globalWords[word].freq++;
+ } else {
+ twCx.globalWords[word] = {
+ "freq" : 1,
+ "annotations" : {}
+ }
+ for (var j in annotations) {
+ if (j != 'default') {
+ twCx.globalWords[word].annotations[j] = 0;
+ }
+ }
+ }
+ for (var j in ann) {
+ if (typeof twCx.globalWords[word].annotations != "undefined") {
+ twCx.globalWords[word].annotations[ann[j]]++;
+ }
+ }
+ }
+ });
+
+ _(twCx.suggestCount).each(function(_k) {
+ if (tweet.text.search(_k.rgxp) != -1) {
+ _k.freq++;
+ _(ann).each(function(_a) {
+ _k.annotations[_a] = 1 + ( _k.annotations[_a] || 0 )
+ })
+ }
+ });
+
+
+ var p = twCx.idIndex.length;
+ while (p && tweet.id < twCx.idIndex[p-1]) {
+ p--;
+ }
+ twCx.tweets.splice(p, 0, tweet);
+ twCx.idIndex.splice(p, 0, tweet.id);
+
+ if (!twCx.timeline.length) {
+ twCx.timeline = [ populateDateStruct(0, twCx.date_levels[0] * parseInt(tweet.date_value / twCx.date_levels[0])) ]
+ }
+ while (tweet.date_value > twCx.timeline[twCx.timeline.length - 1].end) {
+ twCx.timeline.push( populateDateStruct(0, twCx.timeline[twCx.timeline.length - 1].end) );
+ }
+
+ insertIntoDateStruct(twCx.timeline, tweet);
+}
+
+function getSliceContent(slice) {
+ if (slice.slices) {
+ var result = [];
+ for (var i in slice.slices) {
+ result = result.concat(getSliceContent(slice.slices[i]));
+ }
+ } else {
+ var result = slice.tweets;
+ }
+ return result;
+}
+
+function flattenDateStruct(slices, target_level) {
+ var current_level = slices[0].level,
+ result = [];
+ if (current_level < target_level) {
+ if (slices[0].slices) {
+ for (var i in slices) {
+ result = result.concat(flattenDateStruct(slices[i].slices, target_level));
+ }
+ }
+ }
+ else {
+ for (var i in slices) {
+ result.push({
+ "start" : slices[i].start,
+ "end" : slices[i].end,
+ "tweets" : getSliceContent(slices[i])
+ });
+ }
+ }
+ return result;
+}
+
+function trimFDS() {
+ var slices = flattenDateStruct(twCx.timeline, twCx.timeLevel);
+ if (!slices || !slices.length) {
+ return [];
+ }
+ while (slices[0].tweets.length == 0) {
+ slices.splice(0,1);
+ }
+ while (slices[slices.length - 1].tweets.length == 0) {
+ slices.pop();
+ }
+ var centralTweet = ( twCx.centralTweet ? twCx.centralTweet : twCx.tweets[twCx.tweets.length - 1] ),
+ delta = 30 * twCx.date_levels[twCx.timeLevel],
+ centre = Math.min(slices[slices.length - 1].end - delta , Math.max(slices[0].start + delta, centralTweet.date_value)),
+ min = centre - delta,
+ max = centre + delta;
+ while (slices[0].start < min) {
+ slices.splice(0,1);
+ }
+ while (slices[slices.length - 1].end > max) {
+ slices.pop();
+ }
+ return slices;
+}
+
+function populateDateStruct(level, start) {
+ var end = start + twCx.date_levels[level],
+ struct = {
+ "level" : level,
+ "start" : start,
+ "end" : end
+ };
+ if (level < twCx.date_levels.length - 1) {
+ struct.slices = [];
+ var newstart = start;
+ while (newstart < end) {
+ struct.slices.push(populateDateStruct(level + 1, newstart));
+ newstart += twCx.date_levels[level + 1];
+ }
+ } else {
+ struct.tweets = [];
+ }
+ return struct;
+}
+
+function insertIntoDateStruct(slices, tweet) {
+ var creadate = tweet.date_value;
+ for (var i in slices) {
+ if (creadate < slices[i].end) {
+ if (slices[i].slices) {
+ insertIntoDateStruct(slices[i].slices, tweet);
+ } else {
+ slices[i].tweets.push(tweet.id);
+ }
+ break;
+ }
+ }
+}
+
+function placeHolder(className) {
+ return '<li class="placeholder ' + className + '"></li>';
+}
+
+function tweetById(tweetid) {
+ var pos = _(twCx.idIndex).indexOf(tweetid);
+ return (pos == -1) ? false : twCx.tweets[pos];
+}
+
+function selectTweet(tweetid) {
+ twCx.position = tweetid;
+ twCx.followLast = (twCx.position == twCx.idIndex[twCx.tweets.length - 1]);
+ updateDisplay();
+}
+
+function goToPos(nPos) {
+ twCx.position = twCx.currentIdIndex[Math.min( twCx.currentIdIndex.length - 1, Math.max(0, nPos ) )];
+ twCx.followLast = (!twCx.filtre && nPos == twCx.tweets.length - 1);
+ updateDisplay();
+}
+
+function movePos(delta) {
+ goToPos( delta + _(twCx.currentIdIndex).indexOf(twCx.position) );
+}
+
+function tweetToHtml(tweet, className, elName) {
+
+ function highlight(texte) {
+ return ( twCx.filtre ? texte.replace(twCx.filtre, '<span class="highlight">$1</span>' ) : texte );
+ }
+
+ if (!tweet) {
+ return placeHolder(className);
+ }
+ var el = (elName ? elName : 'li');
+ var html = '<'
+ + el
+ + ' draggable="true" class="tweet '
+ + className
+ + '" id="tweet_'
+ + tweet.id
+ + '" data-title="Tweet by '
+ + _(tweet.user.name).escape()
+ + '" data-description="'
+ + _(tweet.text).escape()
+ + '" data-uri="http://twitter.com/'
+ + tweet.user.screen_name
+ + '/status/'
+ + tweet.id
+ + '"';
+ if (className != 'full') {
+ html += ' onclick="selectTweet(\'' + tweet.id + '\'); return false;"';
+ }
+ html += ' onmouseover="rolloverTweet(\'' + tweet.id + "', " + ( className == 'icons' ) + ');"';
+ if (twCx.followLast && className == 'full' && el == 'li') {
+ html += ' style="display: none"';
+ }
+ html += '>';
+ if (tweet.annotations.length) {
+ html += '<div class="annotations">';
+ for (var i in tweet.annotations) {
+ html += '<div class="annotation" style="width:' + (100/tweet.annotations.length) + '%; background:' + getColor(tweet.annotations[i], (className == 'icons' ? .4 : .25)).hex + '"></div>';
+ }
+ html += '</div>';
+ }
+ html += '<div class="twmain">';
+ var a_user = '<a href="http://twitter.com/' + tweet.user.screen_name + '" onclick="filtrerTexte(\'@' + tweet.user.screen_name + '\'); return false;" target="_blank">';
+ html += '<div class="around_img"><img class="profile_image" src="' + tweet.user.profile_image_url + '" />';
+ if (className == 'full') {
+ html += '<p class="created_at">' + new Date(tweet.date_value).toTimeString().substr(0,8) + '</a></p>';
+ }
+ html += '</div>';
+ if (className != 'icons') {
+ lastend = 0;
+ var txt = '';
+ tweet.html_parts.sort(function(a, b) { return a.start - b.start });
+ _(tweet.html_parts).each(function(_e) {
+ txt += highlight( tweet.text.substring(lastend, _e.start) ) + _e.link + highlight( _e.text ) + '</a>';
+ lastend = _e.end;
+ });
+ txt += highlight( tweet.text.substring(lastend) );
+ html += '<p class="tweet_text"><b>' + a_user + highlight('@' + tweet.user.screen_name) + '</a>' + ( className == 'full' ? ' (' + tweet.user.name + ')</b><br />' : '</b> : ') + txt + '</p>';
+ }
+ html += '</div></' + el + '>';
+ return html;
+}
+
+function tlIdFromPos(x, y, outside) {
+ if (!twCx.tlOnDisplay || !twCx.tlOnDisplay.length) {
+ return;
+ }
+ var ligne = Math.min( twCx.tlOnDisplay.length - 1, Math.max( 0, Math.floor(( twCx.tlHeight - y ) / twCx.scaleY) ) ),
+ colonne = Math.floor(( x - twCx.deltaX ) / twCx.scaleX ),
+ l = 0;
+ if (colonne >= twCx.tlOnDisplay[ligne].totalTweets || colonne < 0 ) {
+ if (outside) {
+ colonne = Math.min( twCx.tlOnDisplay[ligne].totalTweets - 1, Math.max( 0, colonne ));
+ } else {
+ return null;
+ }
+ }
+ for (var i in twCx.tlOnDisplay[ligne].displayData) {
+ var nl = l + twCx.tlOnDisplay[ligne].displayData[i].length;
+ if (colonne < nl) {
+ return {
+ "id" : twCx.tlOnDisplay[ligne].displayData[i][colonne - l],
+ "annotation" : i
+ }
+ }
+ l = nl;
+ }
+}
+
+function tlPosTweet(tweet, annotation) {
+ if (!twCx.tweets) {
+ return;
+ }
+ var x,
+ y,
+ dt = tweet.date_value,
+ ann = ( annotation ? annotation : ( tweet.annotations.length ? tweet.annotations[0] : 'default' ) );
+ for (var i = 0; i < twCx.tlOnDisplay.length; i++) {
+ if (twCx.tlOnDisplay[i].end > dt) {
+ y = twCx.tlHeight - (i + .5) * twCx.scaleY;
+ var l = 0;
+ for (var j in twCx.tlOnDisplay[i].displayData) {
+ if (j == ann) {
+ var p = _(twCx.tlOnDisplay[i].displayData[j]).indexOf(tweet.id);
+ if (p != -1) {
+ x = twCx.deltaX + twCx.scaleX * ( p + l + .5 );
+ }
+ break;
+ }
+ l += twCx.tlOnDisplay[i].displayData[j].length;
+ }
+ break;
+ }
+ }
+ return ( x && y ? { "x" : x, "y" : y } : null);
+}
+
+function rolloverTweet(tweetid, showPopup, annotation) {
+ var t = tweetById(tweetid);
+ if (!t) {
+ return;
+ }
+ var p = tlPosTweet(t, annotation);
+ if (!p) {
+ return;
+ }
+ var ptl = $("#timeline").offset();
+ if (showPopup) {
+ $("#hovercontent").html(tweetToHtml(t, 'full', 'div'));
+ $("#hovertweet").css({
+ "left" : parseInt(ptl.left + p.x) + "px",
+ "top" : parseInt(ptl.top + p.y),
+ "display" : "block"});
+ } else {
+ $("#hovertweet").hide();
+ }
+ for (var i in twCx.relHover) {
+ twCx.relHover[i].remove();
+ }
+ twCx.relHover = drawTweetArcs(t, p, '#303030');
+ twCx.relHover.push(drawTweetPos(p, '#ffffff'));
+}
+
+function drawTweetPos(pos, color) {
+ var rel = twCx.tlPaper.rect(pos.x - .5 * twCx.scaleX, pos.y - .5 * twCx.scaleY, twCx.scaleX, twCx.scaleY);
+ rel.attr({ "stroke" : color, "fill" : color, "fill-opacity" : .25 });
+ return rel;
+}
+
+function drawTweetArcs(tweet, pos, color) {
+
+ var res = [];
+
+ function tweetAndArc(a, b, aorb) {
+ if (a && b) {
+ res.push(drawTweetPos(aorb ? a : b, color));
+ var aa = twCx.tlPaper.path(arc(a,b))
+ .attr({ "stroke" : color, "stroke-width" : 1.5, "stroke-opacity" : .8 });
+ res.push(aa);
+ }
+ }
+
+ if (tweet.retweeted_status_id) {
+ var t = tweetById(tweet.retweeted_status_id);
+ if (t) {
+ tweetAndArc(pos, tlPosTweet(t));
+ }
+ }
+
+ if (tweet.in_reply_to_status_id) {
+ var t = tweetById(tweet.in_reply_to_status_id);
+ if (t) {
+ tweetAndArc(pos, tlPosTweet(t));
+ }
+ }
+
+ if (tweet.backRefs) {
+ for (var i in tweet.backRefs) {
+ var t = tweetById(tweet.backRefs[i].referenced_by_id);
+ if (t) {
+ tweetAndArc(tlPosTweet(t), pos, true);
+ }
+ }
+ }
+
+ return res;
+}
+
+function mouseoverkw() {
+ var _jel = $(this),
+ _off = _jel.offset();
+ _jel.css({
+ color: "#0099ff"
+ });
+ $("#hoverkw")
+ .css({
+ "left" : _off.left + "px",
+ "top" : ( parseInt(_off.top) + ~~ (_jel.height() / 2) ) + "px",
+ "display" : "block"
+ })
+ .attr("kw", _jel.text());
+}
+
+function mouseoutkw() {
+ $("#hoverkw").hide();
+ $(this).css({
+ color: "#000000"
+ });
+}
+
+function makeTagCloud(tab, div) {
+ var minfreq = _(tab).min( function(a) { return a.freq} ).freq,
+ maxfreq = Math.max(minfreq + .1, _(tab).max( function(a) { return a.freq} ).freq),
+ echfreq = 8 / Math.sqrt( maxfreq - minfreq ),
+ html = '';
+ _(tab).each(function(_j) {
+ var maxann = 0,
+ ann = "default";
+ for (var k in _j.annotations) {
+ if (_j.annotations[k] == maxann) {
+ ann = "default";
+ }
+ if (_j.annotations[k] > maxann) {
+ ann = k;
+ maxann = _j.annotations[k];
+ }
+ }
+ if (ann == "default") {
+ var coul = '';
+ } else {
+ var c = getColor(ann, .6),
+ coul = "background: rgba(" + [ Math.floor(c.r), Math.floor(c.g), Math.floor(c.b), ( _j.annotations[ann] / _j.freq )].join(',') + ")";
+ }
+ var fontsize = Math.floor( ( 12 + Math.sqrt( _j.freq - minfreq ) * echfreq ) );
+ html += '<span style="line-height: ' + (8 + fontsize) + 'px; font-size: ' + fontsize + 'px;' + coul + '">' + _j.word + '</span> ';
+ });
+ $(div).html(html);
+ $(div + " span")
+ .mouseover(mouseoverkw)
+ .mouseout(mouseoutkw)
+ .click(function() {
+ $("#hoverkw").toggle();
+ });
+}
+
+function updateDisplay() {
+ if (!twCx.tweets) {
+ return;
+ }
+ if (twCx.filtre) {
+ var tweets = _(twCx.tweets).filter(function(tweet) {
+ var mention = '@' + tweet.user.screen_name;
+ return ( tweet.text.search(twCx.filtre) != -1 ) || ( mention.search(twCx.filtre) != -1 );
+ });
+ $("#inp_q").val(twCx.filtreTexte + ' (' + tweets.length + ' tweets)');
+ if (tweets.length) {
+ var idIndex = _(tweets).map(function(tweet) {
+ return tweet.id;
+ });
+ var p = _(idIndex).indexOf(twCx.position);
+ if (p == -1) {
+ for (p = idIndex.length - 1; p > 0 && idIndex[p] > twCx.position; p--) {
+ }
+ }
+ twCx.position = idIndex[p];
+ twCx.currentIdIndex = idIndex;
+ }
+
+ } else {
+ twCx.currentIdIndex = twCx.idIndex;
+ var tweets = twCx.tweets;
+ var p = _(twCx.idIndex).indexOf(twCx.position);
+ if (p == -1) {
+ p = (twCx.followLast ? twCx.idIndex.length - 1 : 0);
+ }
+ }
+
+
+ var l = tweets.length,
+ lines = 0,
+ ppy = 0,
+ html = '',
+ tweetsOnDisplay = [];
+
+ function pushTweet(tp, className) {
+
+ if (tp < l && tp >= 0) {
+
+ html += tweetToHtml(tweets[tp], className);
+
+ tweetsOnDisplay.push(tp);
+
+ } else {
+ html += placeHolder(className);
+ }
+ }
+
+ if (l) {
+
+ twCx.lastScrollPos = Math.floor( twCx.scrollExtent * ( 1 - ( p / l ) ) );
+ $("#scrollcont").scrollTop(twCx.lastScrollPos);
+
+ for (var i = p + 2; i >= p - 2; i--) {
+ pushTweet(i, i === p ? 'full current' : 'full');
+ }
+
+ if (html != twCx.tlBuffer) {
+ $("#tweetlist").html(html);
+ $(".tweet.full").show();
+ twCx.tlBuffer = html;
+ }
+
+ if (twCx.suggestCount.length) {
+ makeTagCloud(twCx.suggestCount, "#suggkw");
+ }
+
+ var tab = _(twCx.globalWords).chain()
+ .map(function(v, k) {
+ return {
+ "word": k,
+ "freq" : v.freq,
+ "annotations" : v.annotations
+ };
+ }).filter(function(v) {
+ return v.freq > 3;
+ }).value();
+
+ if (tab.length) {
+
+ tab = _(tab).sortBy( function(a) { return ( - a.freq ) }).slice(0,40);
+ makeTagCloud(tab,"#motscles");
+ } else {
+ $("#motscles").html('');
+ }
+ twCx.centralTweet = tweets[p];
+ } else {
+ $("#tweetlist").html('');
+ twCx.tlBuffer = '';
+ $("#motscles").html('');
+ }
+
+ twCx.tlOnDisplay = trimFDS();
+ if (!twCx.tlOnDisplay || !twCx.tlOnDisplay.length) {
+ return;
+ }
+ twCx.scaleY = twCx.tlHeight / twCx.tlOnDisplay.length;
+ var maxTweets = 0,
+ startTl = 0,
+ endTl = twCx.tlOnDisplay.length - 1;
+ if (l) {
+ var startTw = tweets[tweetsOnDisplay[tweetsOnDisplay.length - 1]].date_value,
+ endTw = tweets[tweetsOnDisplay[0]].date_value;
+ }
+ for (var i = 0; i < twCx.tlOnDisplay.length; i++) {
+ if (l) {
+ if (startTw >= twCx.tlOnDisplay[i].start && startTw < twCx.tlOnDisplay[i].end) {
+ startTl = i;
+ }
+ if (endTw >= twCx.tlOnDisplay[i].start && endTw < twCx.tlOnDisplay[i].end) {
+ endTl = i;
+ }
+ }
+ var displayData = {};
+ for (var j in annotations) {
+ displayData[j] = [];
+ }
+ for (var j in twCx.tlOnDisplay[i].tweets) {
+ var tweetid = twCx.tlOnDisplay[i].tweets[j],
+ tweet = tweetById(tweetid);
+ if (tweet) {
+ if (tweet.annotations.length) {
+ for (var k in tweet.annotations) {
+ displayData[tweet.annotations[k]].push(tweetid);
+ }
+ } else {
+ displayData['default'].push(tweetid);
+ }
+ }
+ }
+ var nbT = 0;
+ for (var j in displayData) {
+ nbT += displayData[j].length;
+ }
+ maxTweets = Math.max(maxTweets, nbT);
+ twCx.tlOnDisplay[i].displayData = displayData;
+ twCx.tlOnDisplay[i].totalTweets = nbT;
+ }
+ twCx.scaleX = ( twCx.tlWidth - twCx.deltaX ) / maxTweets;
+ twCx.tlPaper.clear();
+ twCx.relHover = null;
+
+ // Dessin de la correspondance liste-timeline
+ if (l) {
+ var startY = twCx.tlHeight - startTl * twCx.scaleY,
+ endY = twCx.tlHeight - ( endTl + 1 ) * twCx.scaleY,
+ path = "M0 " + twCx.tlHeight + "C" + .7*twCx.deltaX + " " + twCx.tlHeight + " " + .3*twCx.deltaX + " " + startY + " " + twCx.deltaX + " " + startY + "L" + twCx.tlWidth + " " + startY + "L" + twCx.tlWidth + " " + endY + "L" + twCx.deltaX + " " + endY + "C" + .3*twCx.deltaX + " " + endY + " " + .7*twCx.deltaX + " 0 0 0";
+ twCx.tlPaper.path( path ).attr({ "stroke" : "none", "fill" : "#8080c0", "opacity" : .2 });
+ }
+ // dessin de la date de début
+
+ twCx.tlPaper.text(twCx.deltaX / 2, twCx.tlHeight - 7, new Date(twCx.tlOnDisplay[0].start).toTimeString().substr(0,5))
+ .attr({ "text-anchor" : "middle", "font-size": "9px", "fill": "#cccccc" });
+
+ // dessin de la date de fin
+
+ twCx.tlPaper.text(twCx.deltaX / 2, 7, new Date(twCx.tlOnDisplay[twCx.tlOnDisplay.length - 1].end).toTimeString().substr(0,5))
+ .attr({ "text-anchor" : "middle", "font-size": "9px", "fill": "#cccccc" });
+
+ for (var i = 0; i < twCx.tlOnDisplay.length; i++) {
+ var n = 0,
+ posY = twCx.tlHeight - ( i + 1 ) * twCx.scaleY;
+ for (var j in twCx.tlOnDisplay[i].displayData) {
+ var ll = twCx.tlOnDisplay[i].displayData[j].length;
+ if (ll > 0) {
+ twCx.tlPaper.rect( twCx.deltaX + n * twCx.scaleX, posY, ll * twCx.scaleX, twCx.scaleY )
+ .attr({"stroke" : "none", "fill" : getColor(j, .4).hex });
+ n += ll;
+ }
+ }
+
+ // Si on est à une demi-heure, on trace un axe secondaire + heure
+
+ if (i < twCx.tlOnDisplay.length - 1 && !(twCx.tlOnDisplay[i].end % 1800000)) {
+ twCx.tlPaper.path("M0 "+posY+"L" + twCx.tlWidth +" "+posY).attr({"stroke":"#555"});
+ twCx.tlPaper.text(twCx.deltaX / 2, posY, new Date(twCx.tlOnDisplay[i].end).toTimeString().substr(0,5)).attr({ "text-anchor" : "middle", "font-size": "9px", "fill": "#cccccc" });
+ }
+ }
+
+ // dessin du tweet courant
+
+ if (l) {
+
+ if (twCx.filtre) {
+ for (var i = 0; i < tweets.length; i++) {
+ if (i != p) {
+ var pos = tlPosTweet(tweets[i]);
+ if (pos) {
+ drawTweetPos(pos, "#ffccff");
+ }
+ }
+ }
+
+ }
+
+ var posp = tlPosTweet(tweets[p]);
+ if (posp) {
+
+ drawTweetPos(posp, "#ffff00");
+ var yy = posp.y - .5 * twCx.scaleY,
+ ppy = $(".tweet.current").offset().top - $("#tweetlist").offset().top
+ path = "M0 " + ppy + "C" + ( .7 * twCx.deltaX ) + " " + ppy + " " + ( .2 * twCx.deltaX ) + " " + yy + " " + ( twCx.deltaX ) + " " + yy + "L" + ( posp.x - .5 * twCx.scaleX ) + " " + yy;
+ yy = posp.y + .5 * twCx.scaleY;
+ ppy += $(".tweet.full").height();
+ path += "L" + ( posp.x - .5 * twCx.scaleX ) + " " + yy + "L" + twCx.deltaX + " " + yy + "C" + ( .2 * twCx.deltaX ) + " " + yy + " " + ( .7 * twCx.deltaX ) + " " + ppy + " 0 " + ppy;
+ twCx.tlPaper.path( path ).attr({"stroke":"#ffff00", "fill" : "#ffff00", "fill-opacity" : .15});
+
+ drawTweetArcs(tweets[p], posp, '#800080');
+ }
+ }
+}
+
+function filtrerAnnotation(annotation) {
+ if (annotations[annotation]) {
+ effectuerFiltrage(annotations[annotation].display_name,
+ new RegExp( "(" + _(annotations[annotation].keywords).map(function(a) { return a.source }).join("|") + ")", "gim" ) );
+ } else {
+ effectuerFiltrage('', null)
+ }
+}
+
+function filtrerTexte(valeur) {
+ effectuerFiltrage( valeur, valeur ? new RegExp("(" + valeur.replace(/(\W)/g, '\\$1') + ")" ,'gim') : null );
+}
+
+function effectuerFiltrage(filtreTexte, tabRegexp) {
+ $("#recherche_annot").slideUp();
+ $("#inp_q").val(filtreTexte).attr("class","rechercheCourante");
+ twCx.filtreTexte = filtreTexte;
+ twCx.filtre = tabRegexp;
+ twCx.followLast = !tabRegexp && (twCx.position == twCx.idIndex[twCx.idIndex.length - 1]);
+ updateDisplay();
+}
+
+function clicTl(evt) {
+ var o = $("#timeline").offset();
+ if (twCx.tlMouseClicked && twCx.tlMouseMoved) {
+ var twid = tlIdFromPos(evt.pageX - o.left + twCx.refPosTl.x - twCx.refMouse.x, evt.pageY - o.top + twCx.refPosTl.y - twCx.refMouse.y, true);
+ if (twid) {
+ selectTweet(twid.id);
+ }
+ } else {
+ var twid = tlIdFromPos(evt.pageX - o.left, evt.pageY - o.top, twCx.tlMouseClicked);
+ if (twCx.tlMouseMoved && !twCx.tlMouseClicked) {
+ if (twid) {
+ rolloverTweet(twid.id, true, twid.annotation);
+ } else {
+ $("#hovertweet").hide();
+ }
+ }
+ if (twCx.tlMouseClicked && !twCx.tlMouseMoved) {
+ if (twid) {
+ selectTweet(twid.id);
+ }
+ }
+ }
+}
+
+function loadTweets(tweets, append) {
+ if (!append) {
+ twCx.timeline = [];
+ twCx.idIndex = [];
+ twCx.tweets = [];
+ }
+ for (var i in tweets) {
+ addTweet(tweets[i]);
+ }
+ if (twCx.followLast) {
+ twCx.position = twCx.idIndex[twCx.tweets.length - 1];
+ }
+ updateDisplay();
+}
+
+function focusOutRecherche() {
+ $("#recherche_annot").slideUp();
+ var inpq = $("#inp_q"),
+ val = inpq.val();
+ if (val == '' || val == twCx.filtreTexte) {
+ if (twCx.filtre) {
+ inpq.attr("class", "rechercheCourante").val(twCx.filtreTexte);
+ } else {
+ inpq.attr("class", "greyed").val(l10n.rechercher);
+ }
+ }
+}
+
+function chaineTimeZoom() {
+ var chaine = "",
+ t = twCx.date_levels[twCx.timeLevel],
+ h = 3600*1000,
+ m = 60*1000,
+ s = 1000,
+ heures = Math.floor(t/h);
+ if (heures) { chaine += heures + ' h. ' };
+ t -= (heures * h);
+ var minutes = Math.floor(t/m);
+ if (minutes) { chaine += minutes + ' min. ' };
+ t -= (minutes * m);
+ if (t) { chaine += Math.floor(t/s) + ' sec.' }
+ $("#time_scale").html(chaine);
+ $("#time_zoomout").attr("class",(twCx.timeLevel == 0 ? "inactive" : ""));
+ $("#time_zoomin").attr("class",(twCx.timeLevel == twCx.date_levels.length - 1 ? "inactive" : ""));
+}
+
+$(document).ready(function() {
+ twCx.tlWidth = $("#timeline").width();
+ twCx.tlHeight = $("#timeline").height();
+ twCx.tlPaper = Raphael("timeline", twCx.tlWidth, twCx.tlHeight);
+
+ connectTweets();
+
+ var html = '';
+ for (var j in annotations) {
+ if (j != "default") {
+ html += '<a href="#" style="background: ' + getColor(j, .6).hex + ';" onclick=filtrerAnnotation(\'' + j + '\'); return false;">' + annotations[j].display_name + '</a> '
+ }
+ }
+ $("#rech_list_annot").html(html);
+
+ chaineTimeZoom();
+
+ $("#tweetlist").mousewheel(function(e, d) {
+ twCx.wheelDelta += d;
+ if (Math.abs(twCx.wheelDelta) >= 1) {
+ movePos( parseInt(twCx.wheelDelta) );
+ twCx.wheelDelta = 0;
+ }
+ return false;
+ });
+ $("#tweetlist").delegate(".tweet", "dragstart", function(e) {
+ var div = document.createElement('div');
+ div.appendChild(this.cloneNode(true));
+ try {
+ e.originalEvent.dataTransfer.setData("text/html",div.innerHTML);
+ }
+ catch(err) {
+ e.originalEvent.dataTransfer.setData("text",div.innerHTML);
+ }
+ });
+ $("#timeline").mousewheel(function(e, d) {
+ twCx.wheelDelta += d;
+ if (Math.abs(twCx.wheelDelta) >= 1) {
+ if (twCx.wheelDelta > 0) {
+ tl = Math.min(twCx.date_levels.length - 1, twCx.timeLevel + 1);
+ } else {
+ tl = Math.max(0, twCx.timeLevel - 1);
+ }
+ if (tl != twCx.timeLevel) {
+ twCx.timeLevel = tl;
+ chaineTimeZoom();
+ updateDisplay();
+ }
+ twCx.wheelDelta = 0;
+ }
+ return false;
+ });
+ $("#time_zoomin").click(function() {
+ if (twCx.timeLevel < twCx.date_levels.length - 1) {
+ twCx.timeLevel++;
+ chaineTimeZoom();
+ updateDisplay();
+ }
+ });
+ $("#time_zoomout").click(function() {
+ if (twCx.timeLevel > 0) {
+ twCx.timeLevel--;
+ chaineTimeZoom();
+ updateDisplay();
+ }
+ });
+ $("#timeline, #tweetlist").mouseout(function() {
+ twCx.tlMouseClicked = false;
+ twCx.tlMouseMoved = false;
+ $("#hovertweet").hide();
+ });
+ $("#timeline").mousemove(function(evt) {
+ twCx.tlMouseMoved = true;
+ clicTl(evt);
+ }).mousedown(function(evt) {
+ twCx.tlMouseClicked = true;
+ twCx.tlMouseMoved = false;
+ var o = $(this).offset();
+ twCx.refMouse = { x : evt.pageX - o.left, y : evt.pageY - o.top };
+ twCx.refPosTl = tlPosTweet(tweetById(twCx.position)) || twCx.refMouse;
+ }).mouseup(function(evt) {
+ clicTl(evt);
+ twCx.tlMouseClicked = false;
+ twCx.tlMouseMoved = false;
+ });
+ $("#inp_q").focus(function() {
+ $("#recherche_annot").slideDown();
+ $(this).val($(this).val().replace(/ \(.+\)$/, ''))
+ if ($(this).hasClass("greyed")) {
+ $(this).val("");
+ }
+ $(this).attr("class","");
+ }).focusout(function() {
+ focusOutRecherche();
+ });
+ $("#inp_reset").click(function() {
+ $("#inp_q").val('');
+ if (twCx.filtre) {
+ twCx.filtre = null;
+ updateDisplay();
+ }
+ twCx.filtreTexte = '';
+ focusOutRecherche();
+ return false;
+ })
+ $("#recherche").submit(function(evt) {
+ evt.preventDefault();
+ if (!$("#inp_q").hasClass("greyed")) {
+ var valeur = $("#inp_q").val();
+ filtrerTexte(valeur);
+ }
+ return false;
+ });
+ $("#hoverkw").mouseover(function() {
+ $(this).dequeue().show();
+ }).mouseout(function() {
+ $(this).hide();
+ });
+
+ $("#hkwsearch").click(function() {
+ var _hkw = $("#hoverkw");
+ filtrerTexte(_hkw.attr("kw"));
+ _hkw.hide();
+ return false;
+ });
+ $("#hkwtweet").click(function() {
+ var _hkw = $("#hoverkw");
+ add_grammar(_hkw.attr("kw"));
+ _hkw.hide();
+ return false;
+ });
+ $(".acctitre").click(function() {
+ $(this).next().slideToggle();
+ return false;
+ })
+
+ if (!suggested_keywords.length) {
+ $("#suggkw").parent().hide();
+ }
+
+ setInterval(function() {
+ var sc = $("#scrollcont");
+ if (sc.scrollTop() != twCx.lastScrollPos && twCx.tweets && twCx.currentIdIndex) {
+ var p = Math.floor( twCx.currentIdIndex.length * ( 1 - sc.scrollTop() / twCx.scrollExtent ) );
+ goToPos(p);
+ }
+ }, 100)
+});
+
+function connectTweets() {
+ twCx.tlPaper.clear();
+ var _sq = twCx.tlPaper.rect(0, twCx.tlHeight, twCx.tlWidth, 0)
+ .attr({
+ "stroke" : "none",
+ "fill" : "#8080cc"
+ });
+ var _lb = twCx.tlPaper.text(twCx.tlWidth / 2, twCx.tlHeight / 2, "0 tweet")
+ .attr({
+ "font-size" : "20px",
+ "text-anchor" : "middle"
+ });
+
+ getTweets({
+ "keyword" : tracking_keywords.join(" OR "),
+ "pages" : max_pages,
+ "rpp" : 100,
+ "cbData" : function() {
+ _lb.attr("text", (this.tweets.length - this.currentPage + 1) + " tweets");
+ var _h = twCx.tlHeight * this.currentPage / this.pages;
+ _sq.animate({
+ "y" : twCx.tlHeight - _h,
+ "height" : _h
+ })
+ },
+ "cbEnd" : function() {
+ loadTweets(this.tweets);
+ setInterval(function() {
+ getTweets({
+ "keyword" : tracking_keywords.join(" OR "),
+ "pages" : 1,
+ /*"since_id" : twCx.idIndex[twCx.idIndex.length - 1],*/
+ "rpp" : 100,
+ "cbEnd" : function() {
+ loadTweets(this.tweets, true);
+ }
+ });
+ }, 60000)
+ }
+ });
+}
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/web/cineconf/style.css Fri Jun 07 11:55:46 2013 +0200
@@ -0,0 +1,66 @@
+body {
+ background: #222222;
+}
+
+
+#main {
+ position: absolute;
+ left: 50%; top: 50%; width: 1400px; height: 875px;
+ margin-top: -437px; margin-left: -700px;
+}
+
+#visionplayer_1101 {
+ position: absolute; margin: 5px; width: 836px; height: 440px; left: 34px; top: 247px;
+}
+
+#visionplayer_1101 img {
+ margin: 20px auto;
+}
+
+#vizcontainer {
+ position: absolute; left: 880px; top: 150px; height: 617px; overflow: hidden;
+}
+
+#vizcontainer, #tweetviz {
+ width: 492px;
+}
+
+#tweetlist {
+ background: #222222; color: #cccccc; width: 320px;
+}
+
+li.tweet {
+ background: none;
+}
+
+.full p.tweet_text {
+ color: #ffffff; font-size: 14px;
+}
+
+#inp_q, #recherche_annot {
+ background: #555555; color: #cccccc;
+}
+
+li.tweet, li.placeholder {
+ border-color: #666666;
+}
+
+li.placeholder:nth-child(n+2) {
+ visibility: visible;
+}
+
+li.full {
+ width: 320px; border-right: none;
+}
+
+li.tweet.full.current {
+ width: 310px; border-right: 10px solid #ff0;
+}
+
+li.tweet a {
+ color: #80ccfc;
+}
+
+#tweetlist, #timeline, #scrollcont {
+ height: 590px;
+}
--- a/web/common.php Wed Apr 10 18:38:21 2013 +0200
+++ b/web/common.php Fri Jun 07 11:55:46 2013 +0200
@@ -17,7 +17,7 @@
$ldt_platform = 'http://ldt.iri.centrepompidou.fr/';
$project_url_base = 'ldtplatform/ldt/cljson/id/';
-$C_default_rep = 'edito-1213-06-modeles-economiques';
+$C_default_rep = 'fens2013';
$C_feedback_form_url = 'https://spreadsheets.google.com/spreadsheet/viewform?hl=en_US&formkey=dDZILVdXVHRzd0xhWGVZXzkweHN2RGc6MQ#gid=0';
$archives_list = array(
@@ -36,7 +36,7 @@
'fens2012-edito-datajournalisme',
'fens2012-wiid',
'edito-1213-01-contextes', 'edito-1213-02-collectifs-auteurs',
- "enmi12", "bpidoudou", 'edito-1213-04-lire-ecrire'
+ "enmi12", "bpidoudou", 'edito-1213-04-lire-ecrire', 'eduinov-2013'
);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/web/edito-1213-07-lecteur-auteur-droits/config.php Fri Jun 07 11:55:46 2013 +0200
@@ -0,0 +1,54 @@
+<?php
+$config = array(
+ 'hashtag' => '#edito12',
+
+ 'title' => "Écritures numériques et éditorialisation",
+
+ 'abstract' => "Séminaire « écritures numériques et éditorialisation » :<br /><b>Lecteur, auteur : questions de droits.</b><br/>le 16/05/2013 à Paris 17h30, à Montréal 11h30<br/>Centre Pompidou et Université de Montréal",
+
+ 'description'=> "<h3>Lecteur, auteur : questions de droits.</h3>
+ <p>le 16/05/2013
+ <br/>À Paris : De 17h30 à 19h30, au Centre Pompidou, salle Triangle - entrée libre et gratuite
+ <br />À Montréal : De 11h30 à 13h30, à l'Université de Montréal, salle P217 du Pavillon Roger-Gaudry, 2900, boul. Édouard-Montpetit</p>
+ <p>avec Daniel Caron et Philippe Aigrain.</p>
+ <p>La question des modèles économiques ne peut être affrontée sans une réflexion sur les droits d’auteur. Mais peut-on encore parler d’auteur lorsqu’on se réfère aux producteurs de contenus numériques ? Comment ne pas prendre en compte toutes les pratiques d’éditorialisation de contenus qui produisent des objets sans pour autant pouvoir être considérées comme des gestes “d’auteur” ? Comment adapter l’idée de “droit” à ces nouvelles pratiques ?</p>
+<ul>
+ <li>
+ <p>
+ <b>Lucie Séguin</b> est directrice générale de la recherche et de la politique stratégique de la Bibliothèque et Archives du Canada (LAC).
+ </p>
+ </li>
+ <li>
+ <p>
+ <b>Philippe Aigrain</b>est fondateur de Sopinspace, co-fondateur de la Quadrature du Net, analyste des enjeux politiques, sociaux et culturels des techniques informationnelles et engagé en faveur de la réforme des régimes de droits intellectuels. Auteur de <a href='http://paigrain.debatpublic.net/?page_id=3968' target='_blank'>Sharing</a>, et des blogs <a href='http://paigrain.debatpublic.net/' target='_blank'>Communs</a> et <a href='http://atelierdebricolage.net/' target='_blank'>Atelier de bricolage littéraire</a>.
+ </p>
+ </li>
+</ul>
+ <p>Programme complet sur <a href='http://seminaire.sens-public.org/' target='_blank'>sens-public.org</a></p>
+ ",
+
+ 'link' => 'http://seminaire.sens-public.org',
+
+ 'islive' => true,
+
+ 'keywords' => 'editorialisation, écritures, numérique, iri',
+
+ 'rep' => basename(__DIR__),
+
+ 'partenaires'=> "<a href='http://www.sens-public.org' class='footerLink' target='_blank'>Sens Public</a> | <a href='http://www.iri.centrepompidou.fr/' class='footerLink' target='_blank'>IRI</a> | <a href='http://www.cite.umontreal.ca/' class='footerLink' target='_blank'>Centre de recherche Interdisciplinaire sur les Technologies Émergentes</a> | <a href='http://www.mshparisnord.fr/fr/' class='footerLink' target='_blank'>Maison des Sciences de l'Homme Paris Nord</a>",
+
+ 'client_visual' => 'images/client_visual.jpg',// 480 × 320 pixels
+
+ 'head_logo' => 'images/logo-edito.png', // 171 × 63 pixels
+
+ 'slide_background' => 'images/slide_background.jpg', // 606 × 282 pixels
+
+ 'archive_img' => 'images/archive_img.jpg', // 270 × 150 pixels
+
+ 'archive_title' => "Lecteur, auteur : questions de droits.",
+
+ 'archive_description' => 'par <a href="http://www.sens-public.org" target="_blank">Sens Public</a> et l\'<a href="http://www.iri.centrepompidou.fr" target="_blank">IRI</a><br/>au Centre Pompidou le 16/05/2013 17:30',
+
+ // After the event
+ 'metadata' => "f0a659f2-7ab8-11e2-88b4-00145ea4a2be"
+);
\ No newline at end of file
Binary file web/edito-1213-07-lecteur-auteur-droits/images/archive_img.jpg has changed
Binary file web/edito-1213-07-lecteur-auteur-droits/images/client_visual.jpg has changed
Binary file web/edito-1213-07-lecteur-auteur-droits/images/logo-edito.png has changed
Binary file web/edito-1213-07-lecteur-auteur-droits/images/slide_background.jpg has changed
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/web/edito-1213-07-lecteur-auteur-droits/index.php Fri Jun 07 11:55:46 2013 +0200
@@ -0,0 +1,6 @@
+<?php
+// Permanent redirection
+header("HTTP/1.1 301 Moved Permanently");
+header("Location: client.php");
+exit();
+?>
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/web/eduinov-2013/apprendre-avec-tice-1/config.php Fri Jun 07 11:55:46 2013 +0200
@@ -0,0 +1,118 @@
+<?php
+$config = array(
+ 'hashtag' => '#eduinov',
+
+ 'title' => "Journées de l’Innovation 2013, Peut-on vraiment apprendre avec les TICE ? Comment ? – Partie 1",
+
+ 'description'=> "
+<h3>Innover pour Refonder</h3>
+<p>Programme des Conférences, salle II</p>
+<ul>
+ <li>
+ <p>27 mars 2013 (9 h 30- 11 h 30)</p>
+ <p>Conférence inaugurale :</p>
+ <p>Claude Lelièvre, professeur d'histoire de l'éducation à la Faculté des sciences humaines et sociales Sorbonne (université de Paris V)</p>
+ </li>
+ <li>
+ <h3>Les tout petits à l’école ? Pour quoi faire ?</h3>
+ <p>27 mars 2013 (11 h 30 - 13 h 30)</p>
+ <ul>
+ <li>
+ <p>CONFERENCE 1</p>
+ <p>Bernard Golse, professeur de médecine, psychiatre, Hôpital Necker, Paris</p>
+ </li>
+ <li>
+ <p>CONFERENCE 2</p>
+ <p>Edouard Gentaz, professeur, université de Genève</p>
+ </li>
+ </ul>
+ </li>
+ <li>
+ <h3>Plus de maîtres que de classes : oui, mais comment ?</h3>
+ <p>27 mars 2013 (14 h - 16 h)</p>
+ <ul>
+ <li>
+ <p>CONFERENCE 3</p>
+ <p>Bruno Suchaut, IREDU</p>
+ </li>
+ <li>
+ <p>CONFERENCE 4</p>
+ <p>Pascal Bressoux, directeur de laboratoire des Sciences de l’Education, Université Pierre Mendes France, Grenoble</p>
+ </li>
+ </ul>
+ </li>
+ <li>
+ <h3>L’égalité filles-garçons, la société change, l’école aussi ?</h3>
+ <p>27 mars 2013 (16 h 15 - 18 h)</p>
+ <ul>
+ <li>
+ <p>CONFERENCE 5</p>
+ <p>Marie Duru-Bellat, professeur des universités, Sciences po., Paris</p>
+ </li>
+ </ul>
+ </li>
+ <li>
+ <h3>Cycles, liaison, socle : passons aux actes !</h3>
+ <p>28 mars 2013 (9 h 00 – 10 h 45)</p>
+ <ul>
+ <li>
+ <p>CONFERENCE 7</p>
+ <p>Roger-François Gauthier, IGAENR</p>
+ </li>
+ <li>
+ <p>CONFERENCE 8 </p>
+ <p>Françoise Lantheaume, maitre de conférences, Université Lumière Lyon 2 </p>
+ </li>
+ </ul>
+ </li>
+ <li>
+ <h3>Que nous apprennent les élèves décrocheurs d’école ?</h3>
+ <p>28 mars 2013 (11 h 00 – 13 h 00)</p>
+ <ul>
+ <li>
+ <p>CONFERENCE 9</p>
+ <p>George Pau-Langevin, Ministre déléguée</p>
+ </li>
+ <li>
+ <p>CONFERENCE 10</p>
+ <p>Maryse Esterlé, enseignante-chercheure maîtresse de conférences - Université d'Artois </p>
+ </li>
+ </ul>
+ </li>
+ <li>
+ <h3>Peut-on vraiment apprendre avec les TICE ? Comment ?</h3>
+ <p>28 mars 2013 (13 h 30 – 15 h 30)</p>
+ <ul>
+ <li>
+ <p>CONFERENCE 11</p>
+ <p>David Istance, directeur du CERI-OCDE, les scénarios du futur proche pour l’Ecole </p>
+ </li>
+ <li>
+ <p>CONFERENCE 12</p>
+ <p>Divina Frau- Meigs, professeure à l’Université de Paris III, en sciences de l'information et de la communication et en langues et littératures anglaises et anglo-saxonnes </p>
+ </li>
+ </ul>
+ </li>
+</ul>
+",
+
+ 'link' => 'http://cndpval.cndp.fr/innovation/accueil.html',
+
+ 'keywords' => 'iri, éducation, innovation, éducation nationale',
+
+ 'rep' => 'eduinov-2013/'.basename(__DIR__),
+
+ 'partenaires'=> "<a href='http://www.education.gouv.fr/' class='footerLink' target='_blank'>Ministère de l’Éducation Nationale</a>",
+
+ 'head_logo' => 'images/head-logo.png', // 171 × 63 pixels
+
+ 'archive_img' => 'images/archive_img.jpg', // 270 × 150 pixels
+
+ 'archive_title' => "Peut-on vraiment apprendre avec les TICE ? Comment ? – Partie 1",
+ 'archive_description' => 'Journées de l’Innovation 2013, Ministère de l’Éducation Nationale',
+
+ // After the event
+ 'metadata' => "87abcc34-9d13-11e2-959a-00145ea4a2be",
+
+ 'renkan' => 'http://renkan.iri-research.org/renkan/rest/projects/ef7ea617-df99-4c73-b4b6-0408f2e503f3?callback=?'
+);
\ No newline at end of file
Binary file web/eduinov-2013/apprendre-avec-tice-1/images/archive_img.jpg has changed
Binary file web/eduinov-2013/apprendre-avec-tice-1/images/head-logo.png has changed
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/web/eduinov-2013/apprendre-avec-tice-1/index.php Fri Jun 07 11:55:46 2013 +0200
@@ -0,0 +1,6 @@
+<?php
+// Permanent redirection
+header("HTTP/1.1 301 Moved Permanently");
+header("Location: renkan.php");
+exit();
+?>
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/web/eduinov-2013/apprendre-avec-tice-2/config.php Fri Jun 07 11:55:46 2013 +0200
@@ -0,0 +1,118 @@
+<?php
+$config = array(
+ 'hashtag' => '#eduinov',
+
+ 'title' => "Journées de l’Innovation 2013, Peut-on vraiment apprendre avec les TICE ? Comment ? – Partie 2",
+
+ 'description'=> "
+<h3>Innover pour Refonder</h3>
+<p>Programme des Conférences, salle II</p>
+<ul>
+ <li>
+ <p>27 mars 2013 (9 h 30- 11 h 30)</p>
+ <p>Conférence inaugurale :</p>
+ <p>Claude Lelièvre, professeur d'histoire de l'éducation à la Faculté des sciences humaines et sociales Sorbonne (université de Paris V)</p>
+ </li>
+ <li>
+ <h3>Les tout petits à l’école ? Pour quoi faire ?</h3>
+ <p>27 mars 2013 (11 h 30 - 13 h 30)</p>
+ <ul>
+ <li>
+ <p>CONFERENCE 1</p>
+ <p>Bernard Golse, professeur de médecine, psychiatre, Hôpital Necker, Paris</p>
+ </li>
+ <li>
+ <p>CONFERENCE 2</p>
+ <p>Edouard Gentaz, professeur, université de Genève</p>
+ </li>
+ </ul>
+ </li>
+ <li>
+ <h3>Plus de maîtres que de classes : oui, mais comment ?</h3>
+ <p>27 mars 2013 (14 h - 16 h)</p>
+ <ul>
+ <li>
+ <p>CONFERENCE 3</p>
+ <p>Bruno Suchaut, IREDU</p>
+ </li>
+ <li>
+ <p>CONFERENCE 4</p>
+ <p>Pascal Bressoux, directeur de laboratoire des Sciences de l’Education, Université Pierre Mendes France, Grenoble</p>
+ </li>
+ </ul>
+ </li>
+ <li>
+ <h3>L’égalité filles-garçons, la société change, l’école aussi ?</h3>
+ <p>27 mars 2013 (16 h 15 - 18 h)</p>
+ <ul>
+ <li>
+ <p>CONFERENCE 5</p>
+ <p>Marie Duru-Bellat, professeur des universités, Sciences po., Paris</p>
+ </li>
+ </ul>
+ </li>
+ <li>
+ <h3>Cycles, liaison, socle : passons aux actes !</h3>
+ <p>28 mars 2013 (9 h 00 – 10 h 45)</p>
+ <ul>
+ <li>
+ <p>CONFERENCE 7</p>
+ <p>Roger-François Gauthier, IGAENR</p>
+ </li>
+ <li>
+ <p>CONFERENCE 8 </p>
+ <p>Françoise Lantheaume, maitre de conférences, Université Lumière Lyon 2 </p>
+ </li>
+ </ul>
+ </li>
+ <li>
+ <h3>Que nous apprennent les élèves décrocheurs d’école ?</h3>
+ <p>28 mars 2013 (11 h 00 – 13 h 00)</p>
+ <ul>
+ <li>
+ <p>CONFERENCE 9</p>
+ <p>George Pau-Langevin, Ministre déléguée</p>
+ </li>
+ <li>
+ <p>CONFERENCE 10</p>
+ <p>Maryse Esterlé, enseignante-chercheure maîtresse de conférences - Université d'Artois </p>
+ </li>
+ </ul>
+ </li>
+ <li>
+ <h3>Peut-on vraiment apprendre avec les TICE ? Comment ?</h3>
+ <p>28 mars 2013 (13 h 30 – 15 h 30)</p>
+ <ul>
+ <li>
+ <p>CONFERENCE 11</p>
+ <p>David Istance, directeur du CERI-OCDE, les scénarios du futur proche pour l’Ecole </p>
+ </li>
+ <li>
+ <p>CONFERENCE 12</p>
+ <p>Divina Frau- Meigs, professeure à l’Université de Paris III, en sciences de l'information et de la communication et en langues et littératures anglaises et anglo-saxonnes </p>
+ </li>
+ </ul>
+ </li>
+</ul>
+",
+
+ 'link' => 'http://cndpval.cndp.fr/innovation/accueil.html',
+
+ 'keywords' => 'iri, éducation, innovation, éducation nationale',
+
+ 'rep' => 'eduinov-2013/'.basename(__DIR__),
+
+ 'partenaires'=> "<a href='http://www.education.gouv.fr/' class='footerLink' target='_blank'>Ministère de l’Éducation Nationale</a>",
+
+ 'head_logo' => 'images/head-logo.png', // 171 × 63 pixels
+
+ 'archive_img' => 'images/archive_img.jpg', // 270 × 150 pixels
+
+ 'archive_title' => "Peut-on vraiment apprendre avec les TICE ? Comment ? – Partie 2",
+ 'archive_description' => 'Journées de l’Innovation 2013, Ministère de l’Éducation Nationale',
+
+ // After the event
+ 'metadata' => "f007b7c0-9d13-11e2-b8d2-00145ea4a2be",
+
+ 'renkan' => 'http://renkan.iri-research.org/renkan/rest/projects/ef7ea617-df99-4c73-b4b6-0408f2e503f3?callback=?'
+);
\ No newline at end of file
Binary file web/eduinov-2013/apprendre-avec-tice-2/images/archive_img.jpg has changed
Binary file web/eduinov-2013/apprendre-avec-tice-2/images/head-logo.png has changed
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/web/eduinov-2013/apprendre-avec-tice-2/index.php Fri Jun 07 11:55:46 2013 +0200
@@ -0,0 +1,6 @@
+<?php
+// Permanent redirection
+header("HTTP/1.1 301 Moved Permanently");
+header("Location: renkan.php");
+exit();
+?>
\ No newline at end of file
--- a/web/eduinov-2013/config.php Wed Apr 10 18:38:21 2013 +0200
+++ b/web/eduinov-2013/config.php Fri Jun 07 11:55:46 2013 +0200
@@ -1,8 +1,13 @@
<?php
$config = array(
'event_list' => array(
- 'eduinov-2013/conference',
- 'eduinov-2013/table-ronde'
+ 'eduinov-2013/intervention-ministre',
+ 'eduinov-2013/egalite-filles-garcons',
+ 'eduinov-2013/numerique-outils-usages',
+ 'eduinov-2013/apprendre-avec-tice-1',
+ 'eduinov-2013/apprendre-avec-tice-2',
+ 'eduinov-2013/plus-de-maitres-que-de-classes',
+ 'eduinov-2013/eleves-decrocheurs'
),
'hashtag' => '#eduinov',
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/web/eduinov-2013/egalite-filles-garcons/config.php Fri Jun 07 11:55:46 2013 +0200
@@ -0,0 +1,118 @@
+<?php
+$config = array(
+ 'hashtag' => '#eduinov',
+
+ 'title' => "Journées de l’Innovation 2013, L’égalité filles-garçons, la société change, l’école aussi ?",
+
+ 'description'=> "
+<h3>Innover pour Refonder</h3>
+<p>Programme des Conférences, salle II</p>
+<ul>
+ <li>
+ <p>27 mars 2013 (9 h 30- 11 h 30)</p>
+ <p>Conférence inaugurale :</p>
+ <p>Claude Lelièvre, professeur d'histoire de l'éducation à la Faculté des sciences humaines et sociales Sorbonne (université de Paris V)</p>
+ </li>
+ <li>
+ <h3>Les tout petits à l’école ? Pour quoi faire ?</h3>
+ <p>27 mars 2013 (11 h 30 - 13 h 30)</p>
+ <ul>
+ <li>
+ <p>CONFERENCE 1</p>
+ <p>Bernard Golse, professeur de médecine, psychiatre, Hôpital Necker, Paris</p>
+ </li>
+ <li>
+ <p>CONFERENCE 2</p>
+ <p>Edouard Gentaz, professeur, université de Genève</p>
+ </li>
+ </ul>
+ </li>
+ <li>
+ <h3>Plus de maîtres que de classes : oui, mais comment ?</h3>
+ <p>27 mars 2013 (14 h - 16 h)</p>
+ <ul>
+ <li>
+ <p>CONFERENCE 3</p>
+ <p>Bruno Suchaut, IREDU</p>
+ </li>
+ <li>
+ <p>CONFERENCE 4</p>
+ <p>Pascal Bressoux, directeur de laboratoire des Sciences de l’Education, Université Pierre Mendes France, Grenoble</p>
+ </li>
+ </ul>
+ </li>
+ <li>
+ <h3>L’égalité filles-garçons, la société change, l’école aussi ?</h3>
+ <p>27 mars 2013 (16 h 15 - 18 h)</p>
+ <ul>
+ <li>
+ <p>CONFERENCE 5</p>
+ <p>Marie Duru-Bellat, professeur des universités, Sciences po., Paris</p>
+ </li>
+ </ul>
+ </li>
+ <li>
+ <h3>Cycles, liaison, socle : passons aux actes !</h3>
+ <p>28 mars 2013 (9 h 00 – 10 h 45)</p>
+ <ul>
+ <li>
+ <p>CONFERENCE 7</p>
+ <p>Roger-François Gauthier, IGAENR</p>
+ </li>
+ <li>
+ <p>CONFERENCE 8 </p>
+ <p>Françoise Lantheaume, maitre de conférences, Université Lumière Lyon 2 </p>
+ </li>
+ </ul>
+ </li>
+ <li>
+ <h3>Que nous apprennent les élèves décrocheurs d’école ?</h3>
+ <p>28 mars 2013 (11 h 00 – 13 h 00)</p>
+ <ul>
+ <li>
+ <p>CONFERENCE 9</p>
+ <p>George Pau-Langevin, Ministre déléguée</p>
+ </li>
+ <li>
+ <p>CONFERENCE 10</p>
+ <p>Maryse Esterlé, enseignante-chercheure maîtresse de conférences - Université d'Artois </p>
+ </li>
+ </ul>
+ </li>
+ <li>
+ <h3>Peut-on vraiment apprendre avec les TICE ? Comment ?</h3>
+ <p>28 mars 2013 (13 h 30 – 15 h 30)</p>
+ <ul>
+ <li>
+ <p>CONFERENCE 11</p>
+ <p>David Istance, directeur du CERI-OCDE, les scénarios du futur proche pour l’Ecole </p>
+ </li>
+ <li>
+ <p>CONFERENCE 12</p>
+ <p>Divina Frau- Meigs, professeure à l’Université de Paris III, en sciences de l'information et de la communication et en langues et littératures anglaises et anglo-saxonnes </p>
+ </li>
+ </ul>
+ </li>
+</ul>
+",
+
+ 'link' => 'http://cndpval.cndp.fr/innovation/accueil.html',
+
+ 'keywords' => 'iri, éducation, innovation, éducation nationale',
+
+ 'rep' => 'eduinov-2013/'.basename(__DIR__),
+
+ 'partenaires'=> "<a href='http://www.education.gouv.fr/' class='footerLink' target='_blank'>Ministère de l’Éducation Nationale</a>",
+
+ 'head_logo' => 'images/head-logo.png', // 171 × 63 pixels
+
+ 'archive_img' => 'images/archive_img.jpg', // 270 × 150 pixels
+
+ 'archive_title' => "L’égalité filles-garçons, la société change, l’école aussi ?",
+ 'archive_description' => 'Journées de l’Innovation 2013, Ministère de l’Éducation Nationale',
+
+ // After the event
+ 'metadata' => "dd07416c-9d23-11e2-b8d2-00145ea4a2be",
+
+ "renkan" => "http://renkan.iri-research.org/renkan/rest/projects/78a4eddf-7a75-475a-a880-4513284ec0bf?callback=?",
+);
\ No newline at end of file
Binary file web/eduinov-2013/egalite-filles-garcons/images/archive_img.jpg has changed
Binary file web/eduinov-2013/egalite-filles-garcons/images/head-logo.png has changed
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/web/eduinov-2013/egalite-filles-garcons/index.php Fri Jun 07 11:55:46 2013 +0200
@@ -0,0 +1,6 @@
+<?php
+// Permanent redirection
+header("HTTP/1.1 301 Moved Permanently");
+header("Location: renkan.php");
+exit();
+?>
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/web/eduinov-2013/eleves-decrocheurs/config.php Fri Jun 07 11:55:46 2013 +0200
@@ -0,0 +1,118 @@
+<?php
+$config = array(
+ 'hashtag' => '#eduinov',
+
+ 'title' => "Journées de l’Innovation 2013, Que nous apprennent les élèves décrocheurs d’école ?",
+
+ 'description'=> "
+<h3>Innover pour Refonder</h3>
+<p>Programme des Conférences, salle II</p>
+<ul>
+ <li>
+ <p>27 mars 2013 (9 h 30- 11 h 30)</p>
+ <p>Conférence inaugurale :</p>
+ <p>Claude Lelièvre, professeur d'histoire de l'éducation à la Faculté des sciences humaines et sociales Sorbonne (université de Paris V)</p>
+ </li>
+ <li>
+ <h3>Les tout petits à l’école ? Pour quoi faire ?</h3>
+ <p>27 mars 2013 (11 h 30 - 13 h 30)</p>
+ <ul>
+ <li>
+ <p>CONFERENCE 1</p>
+ <p>Bernard Golse, professeur de médecine, psychiatre, Hôpital Necker, Paris</p>
+ </li>
+ <li>
+ <p>CONFERENCE 2</p>
+ <p>Edouard Gentaz, professeur, université de Genève</p>
+ </li>
+ </ul>
+ </li>
+ <li>
+ <h3>Plus de maîtres que de classes : oui, mais comment ?</h3>
+ <p>27 mars 2013 (14 h - 16 h)</p>
+ <ul>
+ <li>
+ <p>CONFERENCE 3</p>
+ <p>Bruno Suchaut, IREDU</p>
+ </li>
+ <li>
+ <p>CONFERENCE 4</p>
+ <p>Pascal Bressoux, directeur de laboratoire des Sciences de l’Education, Université Pierre Mendes France, Grenoble</p>
+ </li>
+ </ul>
+ </li>
+ <li>
+ <h3>L’égalité filles-garçons, la société change, l’école aussi ?</h3>
+ <p>27 mars 2013 (16 h 15 - 18 h)</p>
+ <ul>
+ <li>
+ <p>CONFERENCE 5</p>
+ <p>Marie Duru-Bellat, professeur des universités, Sciences po., Paris</p>
+ </li>
+ </ul>
+ </li>
+ <li>
+ <h3>Cycles, liaison, socle : passons aux actes !</h3>
+ <p>28 mars 2013 (9 h 00 – 10 h 45)</p>
+ <ul>
+ <li>
+ <p>CONFERENCE 7</p>
+ <p>Roger-François Gauthier, IGAENR</p>
+ </li>
+ <li>
+ <p>CONFERENCE 8 </p>
+ <p>Françoise Lantheaume, maitre de conférences, Université Lumière Lyon 2 </p>
+ </li>
+ </ul>
+ </li>
+ <li>
+ <h3>Que nous apprennent les élèves décrocheurs d’école ?</h3>
+ <p>28 mars 2013 (11 h 00 – 13 h 00)</p>
+ <ul>
+ <li>
+ <p>CONFERENCE 9</p>
+ <p>George Pau-Langevin, Ministre déléguée</p>
+ </li>
+ <li>
+ <p>CONFERENCE 10</p>
+ <p>Maryse Esterlé, enseignante-chercheure maîtresse de conférences - Université d'Artois </p>
+ </li>
+ </ul>
+ </li>
+ <li>
+ <h3>Peut-on vraiment apprendre avec les TICE ? Comment ?</h3>
+ <p>28 mars 2013 (13 h 30 – 15 h 30)</p>
+ <ul>
+ <li>
+ <p>CONFERENCE 11</p>
+ <p>David Istance, directeur du CERI-OCDE, les scénarios du futur proche pour l’Ecole </p>
+ </li>
+ <li>
+ <p>CONFERENCE 12</p>
+ <p>Divina Frau- Meigs, professeure à l’Université de Paris III, en sciences de l'information et de la communication et en langues et littératures anglaises et anglo-saxonnes </p>
+ </li>
+ </ul>
+ </li>
+</ul>
+",
+
+ 'link' => 'http://cndpval.cndp.fr/innovation/accueil.html',
+
+ 'keywords' => 'iri, éducation, innovation, éducation nationale',
+
+ 'rep' => 'eduinov-2013/'.basename(__DIR__),
+
+ 'partenaires'=> "<a href='http://www.education.gouv.fr/' class='footerLink' target='_blank'>Ministère de l’Éducation Nationale</a>",
+
+ 'head_logo' => 'images/head-logo.png', // 171 × 63 pixels
+
+ 'archive_img' => 'images/archive_img.jpg', // 270 × 150 pixels
+
+ 'archive_title' => "Que nous apprennent les élèves décrocheurs d’école ?",
+ 'archive_description' => 'Journées de l’Innovation 2013, Ministère de l’Éducation Nationale',
+
+ // After the event
+ 'metadata' => "8e4112c4-9d23-11e2-85e3-00145ea4a2be",
+
+ 'renkan' => 'http://renkan.iri-research.org/renkan/rest/projects/4c975169-8177-4695-9313-57c254198eb8?callback=?'
+);
\ No newline at end of file
Binary file web/eduinov-2013/eleves-decrocheurs/images/archive_img.jpg has changed
Binary file web/eduinov-2013/eleves-decrocheurs/images/head-logo.png has changed
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/web/eduinov-2013/eleves-decrocheurs/index.php Fri Jun 07 11:55:46 2013 +0200
@@ -0,0 +1,6 @@
+<?php
+// Permanent redirection
+header("HTTP/1.1 301 Moved Permanently");
+header("Location: renkan.php");
+exit();
+?>
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/web/eduinov-2013/intervention-ministre/config.php Fri Jun 07 11:55:46 2013 +0200
@@ -0,0 +1,118 @@
+<?php
+$config = array(
+ 'hashtag' => '#eduinov',
+
+ 'title' => "Journées de l’Innovation 2013, Intervention du ministre de l'Éducation nationale et remise des prix de l’innovation",
+
+ 'description'=> "
+<h3>Innover pour Refonder</h3>
+<p>Programme des Conférences, salle II</p>
+<ul>
+ <li>
+ <p>27 mars 2013 (9 h 30- 11 h 30)</p>
+ <p>Conférence inaugurale :</p>
+ <p>Claude Lelièvre, professeur d'histoire de l'éducation à la Faculté des sciences humaines et sociales Sorbonne (université de Paris V)</p>
+ </li>
+ <li>
+ <h3>Les tout petits à l’école ? Pour quoi faire ?</h3>
+ <p>27 mars 2013 (11 h 30 - 13 h 30)</p>
+ <ul>
+ <li>
+ <p>CONFERENCE 1</p>
+ <p>Bernard Golse, professeur de médecine, psychiatre, Hôpital Necker, Paris</p>
+ </li>
+ <li>
+ <p>CONFERENCE 2</p>
+ <p>Edouard Gentaz, professeur, université de Genève</p>
+ </li>
+ </ul>
+ </li>
+ <li>
+ <h3>Plus de maîtres que de classes : oui, mais comment ?</h3>
+ <p>27 mars 2013 (14 h - 16 h)</p>
+ <ul>
+ <li>
+ <p>CONFERENCE 3</p>
+ <p>Bruno Suchaut, IREDU</p>
+ </li>
+ <li>
+ <p>CONFERENCE 4</p>
+ <p>Pascal Bressoux, directeur de laboratoire des Sciences de l’Education, Université Pierre Mendes France, Grenoble</p>
+ </li>
+ </ul>
+ </li>
+ <li>
+ <h3>L’égalité filles-garçons, la société change, l’école aussi ?</h3>
+ <p>27 mars 2013 (16 h 15 - 18 h)</p>
+ <ul>
+ <li>
+ <p>CONFERENCE 5</p>
+ <p>Marie Duru-Bellat, professeur des universités, Sciences po., Paris</p>
+ </li>
+ </ul>
+ </li>
+ <li>
+ <h3>Cycles, liaison, socle : passons aux actes !</h3>
+ <p>28 mars 2013 (9 h 00 – 10 h 45)</p>
+ <ul>
+ <li>
+ <p>CONFERENCE 7</p>
+ <p>Roger-François Gauthier, IGAENR</p>
+ </li>
+ <li>
+ <p>CONFERENCE 8 </p>
+ <p>Françoise Lantheaume, maitre de conférences, Université Lumière Lyon 2 </p>
+ </li>
+ </ul>
+ </li>
+ <li>
+ <h3>Que nous apprennent les élèves décrocheurs d’école ?</h3>
+ <p>28 mars 2013 (11 h 00 – 13 h 00)</p>
+ <ul>
+ <li>
+ <p>CONFERENCE 9</p>
+ <p>George Pau-Langevin, Ministre déléguée</p>
+ </li>
+ <li>
+ <p>CONFERENCE 10</p>
+ <p>Maryse Esterlé, enseignante-chercheure maîtresse de conférences - Université d'Artois </p>
+ </li>
+ </ul>
+ </li>
+ <li>
+ <h3>Peut-on vraiment apprendre avec les TICE ? Comment ?</h3>
+ <p>28 mars 2013 (13 h 30 – 15 h 30)</p>
+ <ul>
+ <li>
+ <p>CONFERENCE 11</p>
+ <p>David Istance, directeur du CERI-OCDE, les scénarios du futur proche pour l’Ecole </p>
+ </li>
+ <li>
+ <p>CONFERENCE 12</p>
+ <p>Divina Frau- Meigs, professeure à l’Université de Paris III, en sciences de l'information et de la communication et en langues et littératures anglaises et anglo-saxonnes </p>
+ </li>
+ </ul>
+ </li>
+</ul>
+",
+
+ 'link' => 'http://cndpval.cndp.fr/innovation/accueil.html',
+
+ 'keywords' => 'iri, éducation, innovation, éducation nationale',
+
+ 'rep' => 'eduinov-2013/'.basename(__DIR__),
+
+ 'partenaires'=> "<a href='http://www.education.gouv.fr/' class='footerLink' target='_blank'>Ministère de l'Éducation Nationale</a>",
+
+ 'head_logo' => 'images/head-logo.png', // 171 × 63 pixels
+
+ 'archive_img' => 'images/archive_img.jpg', // 270 × 150 pixels
+
+ 'archive_title' => "Intervention du ministre de l’Éducation nationale et remise des prix de l’innovation",
+ 'archive_description' => 'Journées de l’Innovation 2013, Ministère de l’Éducation Nationale',
+
+ // After the event
+ 'metadata' => "4254fd62-9d14-11e2-b8d2-00145ea4a2be",
+
+ 'renkan' => 'http://renkan.iri-research.org/renkan/rest/projects/c867cae8-5d07-4eeb-a68b-616c9471b5fe?callback=?'
+);
\ No newline at end of file
Binary file web/eduinov-2013/intervention-ministre/images/archive_img.jpg has changed
Binary file web/eduinov-2013/intervention-ministre/images/head-logo.png has changed
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/web/eduinov-2013/intervention-ministre/index.php Fri Jun 07 11:55:46 2013 +0200
@@ -0,0 +1,6 @@
+<?php
+// Permanent redirection
+header("HTTP/1.1 301 Moved Permanently");
+header("Location: renkan.php");
+exit();
+?>
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/web/eduinov-2013/numerique-outils-usages/config.php Fri Jun 07 11:55:46 2013 +0200
@@ -0,0 +1,118 @@
+<?php
+$config = array(
+ 'hashtag' => '#eduinov',
+
+ 'title' => "Journées de l’Innovation 2013, Le numérique : des outils, des usages… et après ?",
+
+ 'description'=> "
+<h3>Innover pour Refonder</h3>
+<p>Programme des Conférences, salle II</p>
+<ul>
+ <li>
+ <p>27 mars 2013 (9 h 30- 11 h 30)</p>
+ <p>Conférence inaugurale :</p>
+ <p>Claude Lelièvre, professeur d'histoire de l'éducation à la Faculté des sciences humaines et sociales Sorbonne (université de Paris V)</p>
+ </li>
+ <li>
+ <h3>Les tout petits à l’école ? Pour quoi faire ?</h3>
+ <p>27 mars 2013 (11 h 30 - 13 h 30)</p>
+ <ul>
+ <li>
+ <p>CONFERENCE 1</p>
+ <p>Bernard Golse, professeur de médecine, psychiatre, Hôpital Necker, Paris</p>
+ </li>
+ <li>
+ <p>CONFERENCE 2</p>
+ <p>Edouard Gentaz, professeur, université de Genève</p>
+ </li>
+ </ul>
+ </li>
+ <li>
+ <h3>Plus de maîtres que de classes : oui, mais comment ?</h3>
+ <p>27 mars 2013 (14 h - 16 h)</p>
+ <ul>
+ <li>
+ <p>CONFERENCE 3</p>
+ <p>Bruno Suchaut, IREDU</p>
+ </li>
+ <li>
+ <p>CONFERENCE 4</p>
+ <p>Pascal Bressoux, directeur de laboratoire des Sciences de l’Education, Université Pierre Mendes France, Grenoble</p>
+ </li>
+ </ul>
+ </li>
+ <li>
+ <h3>L’égalité filles-garçons, la société change, l’école aussi ?</h3>
+ <p>27 mars 2013 (16 h 15 - 18 h)</p>
+ <ul>
+ <li>
+ <p>CONFERENCE 5</p>
+ <p>Marie Duru-Bellat, professeur des universités, Sciences po., Paris</p>
+ </li>
+ </ul>
+ </li>
+ <li>
+ <h3>Cycles, liaison, socle : passons aux actes !</h3>
+ <p>28 mars 2013 (9 h 00 – 10 h 45)</p>
+ <ul>
+ <li>
+ <p>CONFERENCE 7</p>
+ <p>Roger-François Gauthier, IGAENR</p>
+ </li>
+ <li>
+ <p>CONFERENCE 8 </p>
+ <p>Françoise Lantheaume, maitre de conférences, Université Lumière Lyon 2 </p>
+ </li>
+ </ul>
+ </li>
+ <li>
+ <h3>Que nous apprennent les élèves décrocheurs d’école ?</h3>
+ <p>28 mars 2013 (11 h 00 – 13 h 00)</p>
+ <ul>
+ <li>
+ <p>CONFERENCE 9</p>
+ <p>George Pau-Langevin, Ministre déléguée</p>
+ </li>
+ <li>
+ <p>CONFERENCE 10</p>
+ <p>Maryse Esterlé, enseignante-chercheure maîtresse de conférences - Université d'Artois </p>
+ </li>
+ </ul>
+ </li>
+ <li>
+ <h3>Peut-on vraiment apprendre avec les TICE ? Comment ?</h3>
+ <p>28 mars 2013 (13 h 30 – 15 h 30)</p>
+ <ul>
+ <li>
+ <p>CONFERENCE 11</p>
+ <p>David Istance, directeur du CERI-OCDE, les scénarios du futur proche pour l’Ecole </p>
+ </li>
+ <li>
+ <p>CONFERENCE 12</p>
+ <p>Divina Frau- Meigs, professeure à l’Université de Paris III, en sciences de l'information et de la communication et en langues et littératures anglaises et anglo-saxonnes </p>
+ </li>
+ </ul>
+ </li>
+</ul>
+",
+
+ 'link' => 'http://cndpval.cndp.fr/innovation/accueil.html',
+
+ 'keywords' => 'iri, éducation, innovation, éducation nationale',
+
+ 'rep' => 'eduinov-2013/'.basename(__DIR__),
+
+ 'partenaires'=> "<a href='http://www.education.gouv.fr/' class='footerLink' target='_blank'>Ministère de l’Éducation Nationale</a>",
+
+ 'head_logo' => 'images/head-logo.png', // 171 × 63 pixels
+
+ 'archive_img' => 'images/archive_img.jpg', // 270 × 150 pixels
+
+ 'archive_title' => "Le numérique : des outils, des usages… et après ?",
+ 'archive_description' => 'Journées de l’Innovation 2013, Ministère de l’Éducation Nationale',
+
+ // After the event
+ 'metadata' => "4eae5822-a8e2-11e2-be4f-00145ea4a2be",
+
+ 'renkan' => 'http://renkan.iri-research.org/renkan/rest/projects/4565e1bd-8085-4117-9b2d-d97c36afb9ee?callback=?'
+);
\ No newline at end of file
Binary file web/eduinov-2013/numerique-outils-usages/images/archive_img.jpg has changed
Binary file web/eduinov-2013/numerique-outils-usages/images/head-logo.png has changed
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/web/eduinov-2013/numerique-outils-usages/index.php Fri Jun 07 11:55:46 2013 +0200
@@ -0,0 +1,6 @@
+<?php
+// Permanent redirection
+header("HTTP/1.1 301 Moved Permanently");
+header("Location: renkan.php");
+exit();
+?>
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/web/eduinov-2013/plus-de-maitres-que-de-classes/config.php Fri Jun 07 11:55:46 2013 +0200
@@ -0,0 +1,118 @@
+<?php
+$config = array(
+ 'hashtag' => '#eduinov',
+
+ 'title' => "Journées de l’Innovation 2013, Plus de maîtres que de classes : oui, mais comment ?",
+
+ 'description'=> "
+<h3>Innover pour Refonder</h3>
+<p>Programme des Conférences, salle II</p>
+<ul>
+ <li>
+ <p>27 mars 2013 (9 h 30- 11 h 30)</p>
+ <p>Conférence inaugurale :</p>
+ <p>Claude Lelièvre, professeur d'histoire de l'éducation à la Faculté des sciences humaines et sociales Sorbonne (université de Paris V)</p>
+ </li>
+ <li>
+ <h3>Les tout petits à l’école ? Pour quoi faire ?</h3>
+ <p>27 mars 2013 (11 h 30 - 13 h 30)</p>
+ <ul>
+ <li>
+ <p>CONFERENCE 1</p>
+ <p>Bernard Golse, professeur de médecine, psychiatre, Hôpital Necker, Paris</p>
+ </li>
+ <li>
+ <p>CONFERENCE 2</p>
+ <p>Edouard Gentaz, professeur, université de Genève</p>
+ </li>
+ </ul>
+ </li>
+ <li>
+ <h3>Plus de maîtres que de classes : oui, mais comment ?</h3>
+ <p>27 mars 2013 (14 h - 16 h)</p>
+ <ul>
+ <li>
+ <p>CONFERENCE 3</p>
+ <p>Bruno Suchaut, IREDU</p>
+ </li>
+ <li>
+ <p>CONFERENCE 4</p>
+ <p>Pascal Bressoux, directeur de laboratoire des Sciences de l’Education, Université Pierre Mendes France, Grenoble</p>
+ </li>
+ </ul>
+ </li>
+ <li>
+ <h3>L’égalité filles-garçons, la société change, l’école aussi ?</h3>
+ <p>27 mars 2013 (16 h 15 - 18 h)</p>
+ <ul>
+ <li>
+ <p>CONFERENCE 5</p>
+ <p>Marie Duru-Bellat, professeur des universités, Sciences po., Paris</p>
+ </li>
+ </ul>
+ </li>
+ <li>
+ <h3>Cycles, liaison, socle : passons aux actes !</h3>
+ <p>28 mars 2013 (9 h 00 – 10 h 45)</p>
+ <ul>
+ <li>
+ <p>CONFERENCE 7</p>
+ <p>Roger-François Gauthier, IGAENR</p>
+ </li>
+ <li>
+ <p>CONFERENCE 8 </p>
+ <p>Françoise Lantheaume, maitre de conférences, Université Lumière Lyon 2 </p>
+ </li>
+ </ul>
+ </li>
+ <li>
+ <h3>Que nous apprennent les élèves décrocheurs d’école ?</h3>
+ <p>28 mars 2013 (11 h 00 – 13 h 00)</p>
+ <ul>
+ <li>
+ <p>CONFERENCE 9</p>
+ <p>George Pau-Langevin, Ministre déléguée</p>
+ </li>
+ <li>
+ <p>CONFERENCE 10</p>
+ <p>Maryse Esterlé, enseignante-chercheure maîtresse de conférences - Université d'Artois </p>
+ </li>
+ </ul>
+ </li>
+ <li>
+ <h3>Peut-on vraiment apprendre avec les TICE ? Comment ?</h3>
+ <p>28 mars 2013 (13 h 30 – 15 h 30)</p>
+ <ul>
+ <li>
+ <p>CONFERENCE 11</p>
+ <p>David Istance, directeur du CERI-OCDE, les scénarios du futur proche pour l’Ecole </p>
+ </li>
+ <li>
+ <p>CONFERENCE 12</p>
+ <p>Divina Frau- Meigs, professeure à l’Université de Paris III, en sciences de l'information et de la communication et en langues et littératures anglaises et anglo-saxonnes </p>
+ </li>
+ </ul>
+ </li>
+</ul>
+",
+
+ 'link' => 'http://cndpval.cndp.fr/innovation/accueil.html',
+
+ 'keywords' => 'iri, éducation, innovation, éducation nationale',
+
+ 'rep' => 'eduinov-2013/'.basename(__DIR__),
+
+ 'partenaires'=> "<a href='http://www.education.gouv.fr/' class='footerLink' target='_blank'>Ministère de l’Éducation Nationale</a>",
+
+ 'head_logo' => 'images/head-logo.png', // 171 × 63 pixels
+
+ 'archive_img' => 'images/archive_img.jpg', // 270 × 150 pixels
+
+ 'archive_title' => "Plus de maîtres que de classes : oui, mais comment ?",
+ 'archive_description' => 'Journées de l’Innovation 2013, Ministère de l’Éducation Nationale',
+
+ // After the event
+ 'metadata' => "5d434ea2-9d24-11e2-b8d2-00145ea4a2be",
+
+ 'renkan' => 'http://renkan.iri-research.org/renkan/rest/projects/6f82e6e7-cf79-40b6-b44c-38a145500287?callback=?'
+);
\ No newline at end of file
Binary file web/eduinov-2013/plus-de-maitres-que-de-classes/images/archive_img.jpg has changed
Binary file web/eduinov-2013/plus-de-maitres-que-de-classes/images/head-logo.png has changed
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/web/eduinov-2013/plus-de-maitres-que-de-classes/index.php Fri Jun 07 11:55:46 2013 +0200
@@ -0,0 +1,6 @@
+<?php
+// Permanent redirection
+header("HTTP/1.1 301 Moved Permanently");
+header("Location: renkan.php");
+exit();
+?>
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/web/enmi12-barcamp/config.php Fri Jun 07 11:55:46 2013 +0200
@@ -0,0 +1,45 @@
+<?php
+$config = array(
+ 'hashtag' => '#enmi12',
+
+ 'title' => "Dispositif(s) de couverture d’événementt : l’expérience des #ENMI12",
+
+ 'abstract' => "<b>Dispositif(s) de couverture d’événement :<br />l’expérience des #ENMI12</b><br/>le 15/04/2013 à 10h00<br/>Centre Pompidou, Salle Triangle",
+
+ 'description'=> "<h3>Atelier enmi12.org</h3>
+ <p>le 15/04/2013
+ <br/>À Paris : De 10h00 à 17h00, au Centre Pompidou, salle Triangle - entrée gratuite sur inscription</p>
+ <p>En décembre dernier, l’<a href='http://www.iri.centrepompidou.fr/' target='_blank'>IRI</a>, <a href='http://knowtex.com/' target='_blank'>Knowtex</a> et le <a href='http://www.btsmultimedia.fr/' target='_blank'>BTS multimédia du Lycée Jacques Prévert</a> se sont associés pour imaginer un dispositif inédit de couverture de la conférence Les Entretiens du Nouveau Monde Industriel : <a href='http://enmi12.org'>enmi12.org</a></p>
+ <p>Après une préparation de 2 mois, une “newsroom” de 30 étudiants a été mise en place pour “augmenter” collectivement l'expérience de la conférence avec une dizaine d'outils (PolemicTweet, Sharypic, UniShared, Pearltrees, FreeMind...)</p>
+ <p>L’IRI organise le lundi 15 avril un atelier pour revenir sur ce dispositif dont l’aspect transmedia a laissé Louise Merzeau supposer qu’il présentait les clés de la translittératie anticipée par plusieurs penseurs des humanités numériques.</p>
+
+ <p>Inscription sur <a href='http://enmi12.eventbrite.fr/' target='_blank'>eventbrite</a></p>
+ ",
+
+ 'link' => 'http://enmi12.eventbrite.fr/',
+
+ 'islive' => true,
+
+ 'keywords' => 'editorialisation, iri, enmi, entretiens du nouveau monde industriel, barcamp',
+
+ 'rep' => basename(__DIR__),
+
+ 'partenaires'=> "<a href='http://www.iri.centrepompidou.fr/' class='footerLink' target='_blank'>IRI</a>
+ | <a href='http://knowtex.com/' class='footerLink' target='_blank'>Knowtex</a>
+ | <a href='http://www.btsmultimedia.fr/' class='footerLink' target='_blank'>BTS multimédia du Lycée Jacques Prévert</a>",
+
+ 'client_visual' => 'images/large.jpg',// 480 × 320 pixels
+
+ 'head_logo' => 'images/logo-enmi.png', // 171 × 63 pixels
+
+ 'slide_background' => 'images/slider.jpg', // 606 × 282 pixels
+
+ 'archive_img' => 'images/small.jpg', // 270 × 150 pixels
+
+ 'archive_title' => "Modèles économiques de l’écriture numérique.",
+
+ 'archive_description' => 'par l\'<a href="http://www.iri.centrepompidou.fr" target="_blank">IRI</a><br/>au Centre Pompidou le 15/04/2013 10:00',
+
+ // After the event
+ 'metadata' => "f0a659f2-7ab8-11e2-88b4-00145ea4a2be"
+);
\ No newline at end of file
Binary file web/enmi12-barcamp/images/large.jpg has changed
Binary file web/enmi12-barcamp/images/logo-enmi.png has changed
Binary file web/enmi12-barcamp/images/slider.jpg has changed
Binary file web/enmi12-barcamp/images/small.jpg has changed
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/web/enmi12-barcamp/index.php Fri Jun 07 11:55:46 2013 +0200
@@ -0,0 +1,6 @@
+<?php
+// Permanent redirection
+header("HTTP/1.1 301 Moved Permanently");
+header("Location: client.php");
+exit();
+?>
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/web/fens2013/client.php Fri Jun 07 11:55:46 2013 +0200
@@ -0,0 +1,6 @@
+<?php
+// Permanent redirection
+header("HTTP/1.1 301 Moved Permanently");
+header("Location: select.php");
+exit();
+?>
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/web/fens2013/config.php Fri Jun 07 11:55:46 2013 +0200
@@ -0,0 +1,41 @@
+<?php
+$config = array(
+ 'event_list' => array(
+ 'fens2013/mashupfilmfestival',
+ 'fens2013/design-metadata',
+ 'fens2013/museo-cultural-data',
+ 'fens2013/edito-verite-fiction',
+ ),
+ 'hashtag' => '#fens2013',
+ 'date' => '13-21.03.2013',
+ 'heure' => '',
+ 'place' => 'Centre Pompidou et Forum des Images, Paris',
+ 'duration' => '',
+
+ 'title' => "Festival Futur en Seine 2013",
+ 'abstract' => "<b>Festival Futur en Seine</b><br/>du 13 au 21 juin 2013 à Paris",
+
+ 'description'=> "<p></p>",
+ 'rep' => basename(__DIR__),
+ 'link' => 'http://www.futur-en-seine.fr/',
+ 'keywords' => 'futur en seine, festival, innovation, nouvelles technologies, paris, centre pompidou, iri',
+ 'partenaires'=> "<a href='http://www.futur-en-seine.fr/'
+class='footerLink' target='_blank'> Futur En Seine </a>
+| <a href='http://www.capdigital.com/'
+class='footerLink' target='_blank'> Cap Digital </a>
+| <a href='http://www.iri.centrepompidou.fr/' class='footerLink' target='_blank'>
+IRI </a>",
+
+ 'client_visual' => "images/client_visual.jpg", // 501 × 376 pixels
+
+ 'head_logo' => 'images/fens-iri.png', // 171 × 63 pixels
+
+ 'slide_background' => "images/slide-fens.jpg", // 606 × 282 pixels
+
+ 'archive_img' => "images/achive_img.jpg", // 270 × 150 pixels
+
+ 'archive_title' => "Futur en Seine - Page d'accueil",
+ 'archive_description' => "par <a href=\"http://www.iri.centrepompidou.fr/\" target=\"_blank\">IRI</a> et <a href=\"http://www.capdigital.com/\" target=\"_blank\">Cap Digital</a> du 13 au 21 juin 2013",
+
+ // After the event
+);
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/web/fens2013/design-metadata/config.php Fri Jun 07 11:55:46 2013 +0200
@@ -0,0 +1,109 @@
+<?php
+$config = array(
+ 'tracking_keywords' => array('#designmd'),
+ 'hashtag' => '#fens2012 #designmd',
+ 'date' => '17.06.2012',
+ 'heure' => '18h00-19h30',
+ 'place' => 'Salle Triangle - Centre Pompidou',
+
+ 'title' => "Futur En Seine 2012 - Pitch Forum Design Metadata",
+
+ 'abstract' =>
+"Pitch Forum :<br />
+<b>Lancement de l'exposition « Design Meta Data ».</b><br/>le 17/06/2013 de 18h00 à 19h30<br/>
+@ Salle Triangle (Centre Pompidou) - entrée libre et gratuite",
+
+ 'description'=>
+"<p>le 17/06/2012 de 18h00 à 19h30<br/>@ Salle Triangle (Centre Pompidou) - entrée libre et gratuite</p>
+<p>L’atelier Design Metadata a proposé cette
+année aux étudiants de travailler sur la
+notion de mashup et de remix comme vecteurs
+d’appropriation et d’écritures nouvelles. Appliqué
+au texte, au son, à l’image ou à la vidéo, le
+mashup ouvre des horizons d’exploration, que
+ce soit dans le design de ses interfaces comme
+dans ses usages, à la croisée de nouvelles
+dynamiques sociales et de scénarios. Le mashup
+peut être considéré comme une nouvelle forme
+d’éditorialisation, s’inscrivant dans la continuité
+des recherches de l’Iri sur l’indexation, la
+navigation et les dispositifs de contribution.
+Design et développement sont ici étroitement
+associés dans un partenariat associant cinq
+écoles prestigieuses : L’Ensad, l’Ensci, l’Esilv, Hétic et Strate Collège</p>
+<p>Le Pitch Forum marquera le lancement
+de l’exposition Design Metadata.
+Lors de celui-ci, les étudiants participants présenteront leurs projets.</p>
+<p><b>FENS WALL</b>, une installation diffusant les médias
+liés à Futur en Seine et donnant à chaque visiteur
+la possibilité d'explorer ceux-ci, de créer une
+sélection, de la sauvegarder et de la partager à
+l'aide d'une interface gestuelle. Conçu et réalisé par Anthony Auffret, Claire Eliot, Axel Fert, Paul
+Idrobo Castro, Coline Malivel, Tamara Sosak,
+Martin Tardy</p>
+<p><b>KÉP</b>, un outil ludique de création et de partage
+de rébus, qui transforme les phrases proposées
+par les utilisateurs en des mashups composés
+d'images et de vidéos puisés sur internet. Conçu et réalisé par Marc-Anthony Benoît, Cristián Canto, Szonja
+Iván, Clément Procureur</p>
+<p><b>CODEPLAY</b>, Un jeu pour initier les enfants
+aux fondamentaux de la programmation
+informatique, combinant une interface tangible
+prenant la forme d'objets à assembler,
+correspondant aux structures du code, et un
+affichage sur tablette. Conçu et réalisé par Pierre Berthelot Kleck, Damien Degrémont,
+Valentin Fontaine, Jade Gardais, Pauline Gourlet,
+Jean-Baptiste Minvielle</p>
+<p><b>MUP</b>, une plateforme pour remixer librement
+n'importe quelle page web: Entrez une URL puis
+éditez le contenu de la page, dessinez par dessus
+et rajoutez des médias. Conçu et réalisé par
+Tom Forlini, Shinhyung Kim, Kevin La Rosa, Marc
+Leroy, Cyril Zhao</p>
+<h3>Autres événements PolemicTweet sur Futur En Seine 2013</h3>
+<ul>
+ <li>
+ <a href='http://polemictweet.com/fens2013/mashupfilmfestival'>Mashup Film Festival, Conférence-Manifeste</a>,
+ le samedi 15 juin 2012 de 14h30 à 19h00, Forum des Images.
+ </li>
+ <li>
+ <a href='http://polemictweet.com/fens2013/design-metadata'>Pitch Forum Design Metadata</a>,
+ le lundi 17 juin 2012 de 18h00 à 19h30, Salle Triangle, Centre Pompidou.
+ </li>
+ <li>
+ <a href='http://polemictweet.com/fens2013/museo-cultural-data'>Séminaire « Muséologie, muséographie et nouvelles formes d'adresse au public » : Culture et Open Data</a>,
+ le mardi 18 juin 2012 de 17h00 à 20h00, Salle Triangle, Centre Pompidou.
+ </li>
+ <li>
+ <a href='http://polemictweet.com/fens2013/edito-verite-fiction'>Séminaire « écritures numériques et éditorialisation » : Vérité et fiction</a>,
+ le jeudi 20 juin 2012 de 17h30 à 19h30, Salle Triangle, Centre Pompidou.
+ </li>
+</ul>",
+ 'link' => 'http://www.iri.centrepompidou.fr/evenement/design-metadata-2013-2/',
+ 'islive' => true,
+ 'keywords' => 'futur en seine, design, metadata, métadonnées, ingénierie, étudiants, projets, pitch, présentation, iri, mashup, remix',
+ 'rep' => basename(__DIR__),
+ 'partenaires'=>
+"<a href='http://www.futur-en-seine.fr/' class='footerLink' target='_blank'>Futur en Seine 2012</a>
+ | <a href='http://www.capdigital.com/' class='footerLink' target='_blank'>Cap Digital</a>
+ | <a href='http://www.ensci.com/' class='footerLink' target='_blank'>Ensci</a>
+ | <a href='http://www.ensad.fr/' class='footerLink' target='_blank'>Ensad</a>
+ | <a href='http://www.hetic.net/' class='footerLink' target='_blank'>Hétic</a>
+ | <a href='http://www.stratecollege.fr/' class='footerLink' target='_blank'>Strate Collège</a>
+ | <a href='http://www.esilv.fr/' class='footerLink' target='_blank'>ESILV</a>
+ | <a href='http://www.iri.centrepompidou.fr/' class='footerLink' target='_blank'>IRI</a>",
+
+ 'client_visual' => 'images/big-designmd.jpg',// 480 × 320 pixels
+
+ 'head_logo' => 'images/fens-pt.png', // 171 × 63 pixels
+
+ 'slide_background' => 'images/slide-designmd.jpg', // 606 × 282 pixels
+
+ 'archive_img' => 'images/archive-designmd.jpg', // 270 × 150 pixels
+
+ 'archive_title' => "Pitch Forum Design Metadata.",
+ 'archive_description' => 'par l\'<a href="http://www.iri.centrepompidou.fr" target="_blank">IRI</a> au Centre Pompidou<br />le lundi 17 juin 2013 18:00 - 19:30',
+
+ // After the event
+ 'metadata' => "86f2eff0-c9da-11e1-9443-00145ea4a2be",
+);
\ No newline at end of file
Binary file web/fens2013/design-metadata/images/archive-designmd.jpg has changed
Binary file web/fens2013/design-metadata/images/big-designmd.jpg has changed
Binary file web/fens2013/design-metadata/images/fens-pt.png has changed
Binary file web/fens2013/design-metadata/images/slide-designmd.jpg has changed
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/web/fens2013/design-metadata/index.php Fri Jun 07 11:55:46 2013 +0200
@@ -0,0 +1,6 @@
+<?php
+// Permanent redirection
+header("HTTP/1.1 301 Moved Permanently");
+header("Location: client.php");
+exit();
+?>
\ No newline at end of file
Binary file web/fens2013/edito-verite-fiction/images/archive_img.jpg has changed
Binary file web/fens2013/edito-verite-fiction/images/client_visual.jpg has changed
Binary file web/fens2013/edito-verite-fiction/images/fens-pt.png has changed
Binary file web/fens2013/edito-verite-fiction/images/slide_background.jpg has changed
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/web/fens2013/index.php Fri Jun 07 11:55:46 2013 +0200
@@ -0,0 +1,6 @@
+<?php
+// Permanent redirection
+header("HTTP/1.1 301 Moved Permanently");
+header("Location: select.php");
+exit();
+?>
\ No newline at end of file
Binary file web/fens2013/mashupfilmfestival/images/archive-muff.jpg has changed
Binary file web/fens2013/mashupfilmfestival/images/big-muff.jpg has changed
Binary file web/fens2013/mashupfilmfestival/images/fens-pt.png has changed
Binary file web/fens2013/mashupfilmfestival/images/slide-muff.jpg has changed
Binary file web/fens2013/museo-cultural-data/images/archive_img.jpg has changed
Binary file web/fens2013/museo-cultural-data/images/client_visual.jpg has changed
Binary file web/fens2013/museo-cultural-data/images/fens-pt.png has changed
Binary file web/fens2013/museo-cultural-data/images/slide_background.jpg has changed
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/web/museo-1213-06-science-collaborative/config.php Fri Jun 07 11:55:46 2013 +0200
@@ -0,0 +1,50 @@
+<?php
+$config = array(
+ 'hashtag' => '#museoweb',
+
+ 'title' => "Muséologie, muséographie et nouvelles formes d’adresse au public<br />Les enjeux de la contribution",
+
+ 'abstract' => "« <b>La science collaborative</b> » <br/>23/04/2013 à 17h00 dans la classe numérique <br />à la Cité des Sciences et de l'Industrie (Paris)",
+
+ 'description'=> "<p><b>La science collaborative.</b> (le 23/04/2013)</p>
+ <p>La classe numérique se trouve dans le Carrefour Numérique, Cité des sciences et de l'industrie, Niveau -1, 30 avenue Corentin Cariou, 75019 Paris (M° Porte de la Villette).</p>
+<ul>
+ <li>
+ <p><b>Olivier Las Vergnas</b>, Universcience.</p>
+ </li>
+ <li>
+ <p><b>Romain Julliard</b>, Muséum National d'Histoire Naturelle, Vigie-Nature.</p>
+ </li>
+ <li>
+ <p><b>Manuel Zacklad</b>, Conservatoire National des Arts et Métiers, Dicen-IDF (Dispositifs d'Information et Communication à l'Ère Numérique).</p>
+ </li>
+</ul>
+",
+
+ 'link' => 'http://www.iri.centrepompidou.fr/evenement/museologie-museographie-et-nouvelles-formes-dadresse-au-public/?lang=fr_fr',
+
+
+ 'keywords' => 'muséographie, muséologie, collaboration, sciences, science collaborative',
+
+ 'rep' => basename(__DIR__),
+
+ 'partenaires'=> "<a href='http://www.culture.gouv.fr/'
+class='footerLink' target='_blank'> Ministère de la Culture et de la Communication </a>
+| <a href='http://www.iri.centrepompidou.fr/' class='footerLink' target='_blank'>
+IRI </a>",
+
+ 'client_visual' => "images/client_visual.jpg", // 480 x 320 pixels
+
+ 'head_logo' => 'images/head_logo.gif', // 171 × 63 pixels
+
+ 'slide_background' => "images/slide_background.jpg", // 606 × 282 pixels
+
+ 'archive_img' => "images/archive_img.jpg", // 270 × 150 pixels
+
+ 'archive_title' => "Muséologie 2012-2013 - La science collaborative.",
+
+ 'archive_description' => "par <a href=\"http://www.iri.centrepompidou.fr/\" target=\"_blank\">IRI</a> à la Cité des Sciences et de l'Industrie<br/> le mardi 23 avril 2013 | 17:00",
+
+ // After the event
+ 'metadata' => "1d1e91c8-71d4-11e1-99d6-00145ea4a2be"
+);
\ No newline at end of file
Binary file web/museo-1213-06-science-collaborative/images/archive_img.jpg has changed
Binary file web/museo-1213-06-science-collaborative/images/client_visual.jpg has changed
Binary file web/museo-1213-06-science-collaborative/images/head_logo.gif has changed
Binary file web/museo-1213-06-science-collaborative/images/slide_background.jpg has changed
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/web/museo-1213-06-science-collaborative/index.php Fri Jun 07 11:55:46 2013 +0200
@@ -0,0 +1,6 @@
+<?php
+// Permanent redirection
+header("HTTP/1.1 301 Moved Permanently");
+header("Location: client.php");
+exit();
+?>
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/web/museo-1213-07-quels-outils-pour-apprendre/config.php Fri Jun 07 11:55:46 2013 +0200
@@ -0,0 +1,50 @@
+<?php
+$config = array(
+ 'hashtag' => '#museoweb',
+
+ 'title' => "Muséologie, muséographie et nouvelles formes d’adresse au public<br />Les enjeux de la contribution",
+
+ 'abstract' => "« <b>Nouveaux manuels, nouveaux environnements hors universités, nouveaux savoirs, nouvelles disciplines : quels outils pour apprendre ?</b> » <br/>28/05/2013 à 17h00 au Centre Pompidou",
+
+ 'description'=> "<p><b>Nouveaux manuels, nouveaux environnements hors universités, nouveaux savoirs, nouvelles disciplines : quels outils pour apprendre ?</b> (le 28/05/2013)</p>
+<ul>
+ <li>
+ <p><b>Carol Ann O’Hare</b>, Wikimédia France, chargée de mission « recherche et enseignement ».</p>
+ </li>
+ <li>
+ <p><b>Caroline Archat</b>, Institut de Recherche et d’Innovation.</p>
+ </li>
+ <li>
+ <p><b>Bertrand Sajus</b>, Ministère de la Culture et de la Communication, Département des Programmes Numériques.</p>
+ </li>
+</ul>
+",
+
+ 'link' => 'http://www.iri.centrepompidou.fr/evenement/museologie-museographie-et-nouvelles-formes-dadresse-au-public/?lang=fr_fr',
+
+ 'islive' => true,
+
+ 'keywords' => 'muséographie, muséologie, enseignement, apprendre, apprentissage, outils',
+
+ 'rep' => basename(__DIR__),
+
+ 'partenaires'=> "<a href='http://www.culture.gouv.fr/'
+class='footerLink' target='_blank'> Ministère de la Culture et de la Communication </a>
+| <a href='http://www.iri.centrepompidou.fr/' class='footerLink' target='_blank'>
+IRI </a>",
+
+ 'client_visual' => "images/client_visual.jpg", // 480 x 320 pixels
+
+ 'head_logo' => 'images/head_logo.gif', // 171 × 63 pixels
+
+ 'slide_background' => "images/slide_background.jpg", // 606 × 282 pixels
+
+ 'archive_img' => "images/archive_img.jpg", // 270 × 150 pixels
+
+ 'archive_title' => "Muséologie 2012-2013 - Quels outils pour apprendre ?",
+
+ 'archive_description' => "par <a href=\"http://www.iri.centrepompidou.fr/\" target=\"_blank\">IRI</a> au Centre Pompidou<br/> le mardi 28 mai 2013 | 17:00",
+
+ // After the event
+ 'metadata' => "1d1e91c8-71d4-11e1-99d6-00145ea4a2be"
+);
\ No newline at end of file
Binary file web/museo-1213-07-quels-outils-pour-apprendre/images/archive_img.jpg has changed
Binary file web/museo-1213-07-quels-outils-pour-apprendre/images/client_visual.jpg has changed
Binary file web/museo-1213-07-quels-outils-pour-apprendre/images/head_logo.gif has changed
Binary file web/museo-1213-07-quels-outils-pour-apprendre/images/slide_background.jpg has changed
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/web/museo-1213-07-quels-outils-pour-apprendre/index.php Fri Jun 07 11:55:46 2013 +0200
@@ -0,0 +1,6 @@
+<?php
+// Permanent redirection
+header("HTTP/1.1 301 Moved Permanently");
+header("Location: client.php");
+exit();
+?>
\ No newline at end of file
--- a/web/player_embed.php Wed Apr 10 18:38:21 2013 +0200
+++ b/web/player_embed.php Fri Jun 07 11:55:46 2013 +0200
@@ -69,6 +69,11 @@
height: 300,
provider: "rtmp"
},
+ { type: "Slider" },
+ {
+ type: "Controller",
+ disable_annotate_btn: true
+ },
<?php if ($protocol_level > 1): ?>
{
type: "Polemic"
@@ -78,11 +83,6 @@
<?php endif; ?>
},
<?php endif; ?>
- { type: "Slider" },
- {
- type: "Controller",
- disable_annotate_btn: true
- },
{
type: "MultiSegments"
},
--- a/web/polemicaltimeline.php Wed Apr 10 18:38:21 2013 +0200
+++ b/web/polemicaltimeline.php Fri Jun 07 11:55:46 2013 +0200
@@ -75,7 +75,7 @@
<script type="text/javascript" src="<?php echo(registry_url('tracemanager','js'));?>"></script>
<!-- Framework CSS -->
- <link rel="stylesheet" href="<?php echo(registry_url('tweetcast','css'));?>?v=11072012" type="text/css" media="screen, projection"/>
+ <link rel="stylesheet" href="<?php echo(registry_url('tweetcast','css'));?>" type="text/css" media="screen, projection"/>
<link rel="stylesheet" href="<?php echo(registry_url('fancybox','css'));?>" media="screen"/>
<!-- FONT -->
@@ -145,6 +145,11 @@
provider: "rtmp",
autostart: true
},
+ { type: "Slider" },
+ {
+ type: "Controller",
+ disable_annotate_btn: true
+ },
<?php if ($protocol_level > 1): ?>
{
type: "Polemic"
@@ -154,11 +159,6 @@
<?php endif; ?>
},
<?php endif; ?>
- { type: "Slider" },
- {
- type: "Controller",
- disable_annotate_btn: true
- },
<?php if ($protocol_level > 1): ?>
{
type: "Segments",
@@ -196,9 +196,7 @@
};
jQuery(document).ready(function() {
-
<?php if ($show_splash): ?>
- var _fancybox = jQuery.fancybox;
jQuery.fancybox(
jQuery("#splash").html(),
{
@@ -208,7 +206,7 @@
});
jQuery("#fancybox-content form").submit(function() {
- _fancybox.close();
+ jQuery.fancybox.close();
var _checkboxes = [];
jQuery("#fancybox-content .checkbox:checked").each(function() { _checkboxes.push(this.value) });
var _data = {
@@ -242,6 +240,13 @@
'transitionOut' : 'none',
'type' : 'iframe'
});
+ jQuery("#Program").fancybox({
+ type: "iframe",
+ width: 840,
+ height: 640,
+ transitionIn: "none",
+ transitionOut: "none"
+ });
jQuery(".embedbutton").click(function() {
_tracer.trace("Pt_EmbedButtonClicked");
});
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/web/renkan.php Fri Jun 07 11:55:46 2013 +0200
@@ -0,0 +1,205 @@
+<?php
+
+/**
+ * include some common code (like we did in the 90s)
+ * People still do this? ;)
+ */
+$rep = $_REQUEST['rep'];
+include_once dirname(__FILE__).'/'.$rep.'/config.php';
+// configuration
+include 'common.php';
+ // objet actuel
+$baseurl = URL_ROOT;
+
+$head_logo = URL_ROOT."$rep/images/head_logo.gif";
+if($translate->_('config__head_logo') != 'config__head_logo' && $translate->_('config__head_logo') != null ) {
+ $head_logo = URL_ROOT."$rep/".$translate->_('config__head_logo');
+}
+
+$url = (!empty($_SERVER['HTTPS'])) ? "https://".$_SERVER['SERVER_NAME'].$_SERVER['REQUEST_URI'] : "http://".$_SERVER['SERVER_NAME'].$_SERVER['REQUEST_URI'];
+
+?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
+<html lang="<?php echo($actual); ?>">
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+ <title>Polemic tweet - <?php echo($translate->_('config__title')); ?></title>
+ <meta name="keywords" content="<?php echo($translate->_('config__keywords')); ?>">
+
+ <meta name="description" content="<?php echo(strip_tags($translate->_('config__description'))); ?>">
+ <meta name="robots" content="index, follow">
+
+ <!-- JAVASCRIPT -->
+ <script type="text/javascript" src="<?php echo(registry_url('metadataplayer','js'));?>"></script>
+
+ <!-- Framework CSS -->
+ <link rel="stylesheet" href="<?php echo(registry_url('tweetcast','css'));?>?v=11072012" type="text/css" media="screen, projection"/>
+
+ <!-- FONT -->
+ <link href='<?php echo(registry_url('PT-Sans_Narrow','font'));?>' rel='stylesheet' type='text/css'/>
+ <link href='<?php echo(registry_url('PT-Sans','font'));?>' rel='stylesheet' type='text/css'/>
+
+
+ <script type="text/javascript">
+
+ IriSP.libFiles.defaultDir = "<?php echo(registry_url('libdir','js'));?>";
+ IriSP.widgetsDir = "<?php echo(registry_url('ldtwidgets','js'));?>";
+ IriSP.libFiles.locations.jwPlayerSWF = "<?php echo(URL_ROOT); ?>res/mediaplayer/player.swf";
+ IriSP.language = "<?php echo($actual) ?>";
+
+ var _metadata = {
+ url: '<?php echo(get_metadata_url($translate->_('config__metadata')));?>',
+ format: 'ldt'
+ };
+ var _config = {
+ width: 550,
+ container: 'LdtPlayer',
+ default_options: {
+ metadata: _metadata
+ },
+ css:'<?php echo(registry_url('metadataplayer','css'));?>',
+ widgets: [
+ {
+ type: "JwpPlayer",
+ live: true,
+ height: 360,
+ width: 550,
+ provider: "rtmp",
+ autostart: true
+ },
+ { type: "Slider" },
+ {
+ type: "Controller",
+ disable_annotate_btn: true
+ },
+ {
+ type: "Polemic",
+ polemics: []
+ },
+ {
+ type: "Segments",
+ annotation_type: "chap"
+ },
+ {
+ type: "Annotation",
+ annotation_type: "chap"
+ },
+ { type: "Tweet" },
+ { type: "Mediafragment"},
+ {
+ type: "Renkan",
+ container: "RenkanContainer",
+ data: "<?php echo($translate->_('config__renkan'));?>"
+ }
+ ]
+ };
+
+ </script>
+ <script type="text/javascript">
+ var _gaq = _gaq || [];
+ _gaq.push(['_setAccount', 'UA-23581291-1']);
+ _gaq.push(['_trackPageview', location.pathname + location.search + location.hash]);
+ _gaq.push(['_setAllowAnchor', true]);
+
+ (function() {
+ var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
+ ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
+ var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
+ })();
+ </script>
+ <style type="text/css">
+ .ldtrenkan-main {
+ width: 1160px; margin: 0 auto; min-height: 400px; clear:both;
+ }
+ .ldtrenkan-main h1{
+ padding: 10px 0 8px; font-size: 20px; font-family: "PT Sans Narrow"; font-weight: bold; color: #0068C4;
+ }
+ #LdtPlayer {
+ float: left; width: 550px;
+ }
+ #RenkanContainer {
+ float: right; width: 600px; height: 500px; margin-left: 10px;
+ }
+ </style>
+ </head>
+ <body>
+ <div id="sendUsFeedBack"><a href="<?php echo($C_feedback_form_url); ?>" target="_blank"><img src="<?php echo(URL_ROOT); ?>images/sendusfeedback.png"></a></div>
+
+ <!-- tooltip -->
+ </div>
+ <div id="container">
+ <div class="barre">
+ <img id="headlogo" src="<?php echo($head_logo); ?>" width="171" height="63" />
+ <div id="minilogo"></div>
+ <ul class="menu">
+ <li>
+ <a href="<?php echo(URL_ROOT); ?>" class="menuLink">
+ <?php print $translate->_("Accueil"); ?>
+ </a>
+ </li>
+ <li>
+ <a href="<?php echo($translate->_('config__link')); ?>" class="menuLink" target="_blank" id='Program'>
+ <?php print $translate->_("Programme"); ?>
+ </a>
+ </li>
+ <li>
+ <a href="../about.php" class="menuLink" >
+ <?php print $translate->_("A propos"); ?>
+ </a>
+ </li>
+ </ul>
+ <ul class="menu">
+ <li>
+ <a href="<?php URL_ROOT ?>polemicaltimeline.php?lang=ja_JP" class="menuLink" >
+ <img src='<?php echo(URL_ROOT); ?>images/flag_jp.gif'<?php if($actual!="ja_JP"){echo("style='opacity: .5;'"); } ?> />
+ <?php print $translate->_("Japonais"); ?>
+ </a></li>
+ <li>
+ <a href="<?php URL_ROOT ?>polemicaltimeline.php?lang=fr" class="menuLink">
+ <img src='<?php echo(URL_ROOT); ?>images/flag_fr.gif' <?php if($actual!="fr"){echo("style='opacity: .5;'"); } ?> />
+ <?php print $translate->_("Français"); ?>
+ </a>
+ </li>
+ <li>
+ <a href="<?php URL_ROOT ?>polemicaltimeline.php?lang=en" class="menuLink">
+ <img src='<?php echo(URL_ROOT); ?>images/flag_en.gif' <?php if($actual!="en"){echo("style='opacity: .5;'"); } ?> />
+ <?php print $translate->_("Anglais"); ?>
+ </a>
+ </li>
+ </ul>
+
+ <div class="embedbar">
+ <!-- AddThis Button BEGIN -->
+ <div class="addthis_toolbox addthis_default_style addthis_32x32_style">
+ <a class="addthis_button_facebook"></a>
+ <a class="addthis_button_twitter"></a>
+ <a class="addthis_button_email"></a>
+ <a class="addthis_button_compact"></a>
+ </div>
+ <script type="text/javascript" src="http://s7.addthis.com/js/250/addthis_widget.js"></script>
+ <!-- AddThis Button END -->
+ </div>
+ </div>
+
+ <!-- EXPLICATION -->
+
+ </div>
+ <div class="ldtrenkan-main" >
+ <h1><?php echo($translate->_('config__title')); ?></h1>
+ <div id="LdtPlayer"></div>
+ <div id="RenkanContainer"></div>
+ </div>
+ <script type="text/javascript">
+ var _myPlayer = new IriSP.Metadataplayer(_config);
+ </script>
+
+ <div class="footer">
+
+ <hr>
+ <?php echo($translate->_('config__partenaires')); ?>
+ </div>
+
+ </div>
+
+ </body>
+</html>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/web/res/js/backbone-relational.js Fri Jun 07 11:55:46 2013 +0200
@@ -0,0 +1,1860 @@
+/* vim: set tabstop=4 softtabstop=4 shiftwidth=4 noexpandtab: */
+/**
+ * Backbone-relational.js 0.8.5
+ * (c) 2011-2013 Paul Uithol and contributors (https://github.com/PaulUithol/Backbone-relational/graphs/contributors)
+ *
+ * Backbone-relational may be freely distributed under the MIT license; see the accompanying LICENSE.txt.
+ * For details and documentation: https://github.com/PaulUithol/Backbone-relational.
+ * Depends on Backbone (and thus on Underscore as well): https://github.com/documentcloud/backbone.
+ */
+( function( undefined ) {
+ "use strict";
+
+ /**
+ * CommonJS shim
+ **/
+ var _, Backbone, exports;
+ if ( typeof window === 'undefined' ) {
+ _ = require( 'underscore' );
+ Backbone = require( 'backbone' );
+ exports = module.exports = Backbone;
+ }
+ else {
+ _ = window._;
+ Backbone = window.Backbone;
+ exports = window;
+ }
+
+ Backbone.Relational = {
+ showWarnings: true
+ };
+
+ /**
+ * Semaphore mixin; can be used as both binary and counting.
+ **/
+ Backbone.Semaphore = {
+ _permitsAvailable: null,
+ _permitsUsed: 0,
+
+ acquire: function() {
+ if ( this._permitsAvailable && this._permitsUsed >= this._permitsAvailable ) {
+ throw new Error( 'Max permits acquired' );
+ }
+ else {
+ this._permitsUsed++;
+ }
+ },
+
+ release: function() {
+ if ( this._permitsUsed === 0 ) {
+ throw new Error( 'All permits released' );
+ }
+ else {
+ this._permitsUsed--;
+ }
+ },
+
+ isLocked: function() {
+ return this._permitsUsed > 0;
+ },
+
+ setAvailablePermits: function( amount ) {
+ if ( this._permitsUsed > amount ) {
+ throw new Error( 'Available permits cannot be less than used permits' );
+ }
+ this._permitsAvailable = amount;
+ }
+ };
+
+ /**
+ * A BlockingQueue that accumulates items while blocked (via 'block'),
+ * and processes them when unblocked (via 'unblock').
+ * Process can also be called manually (via 'process').
+ */
+ Backbone.BlockingQueue = function() {
+ this._queue = [];
+ };
+ _.extend( Backbone.BlockingQueue.prototype, Backbone.Semaphore, {
+ _queue: null,
+
+ add: function( func ) {
+ if ( this.isBlocked() ) {
+ this._queue.push( func );
+ }
+ else {
+ func();
+ }
+ },
+
+ process: function() {
+ while ( this._queue && this._queue.length ) {
+ this._queue.shift()();
+ }
+ },
+
+ block: function() {
+ this.acquire();
+ },
+
+ unblock: function() {
+ this.release();
+ if ( !this.isBlocked() ) {
+ this.process();
+ }
+ },
+
+ isBlocked: function() {
+ return this.isLocked();
+ }
+ });
+ /**
+ * Global event queue. Accumulates external events ('add:<key>', 'remove:<key>' and 'change:<key>')
+ * until the top-level object is fully initialized (see 'Backbone.RelationalModel').
+ */
+ Backbone.Relational.eventQueue = new Backbone.BlockingQueue();
+
+ /**
+ * Backbone.Store keeps track of all created (and destruction of) Backbone.RelationalModel.
+ * Handles lookup for relations.
+ */
+ Backbone.Store = function() {
+ this._collections = [];
+ this._reverseRelations = [];
+ this._orphanRelations = [];
+ this._subModels = [];
+ this._modelScopes = [ exports ];
+ };
+ _.extend( Backbone.Store.prototype, Backbone.Events, {
+ /**
+ * Create a new `Relation`.
+ * @param {Backbone.RelationalModel} [model]
+ * @param {Object} relation
+ * @param {Object} [options]
+ */
+ initializeRelation: function( model, relation, options ) {
+ var type = !_.isString( relation.type ) ? relation.type : Backbone[ relation.type ] || this.getObjectByName( relation.type );
+ if ( type && type.prototype instanceof Backbone.Relation ) {
+ new type( model, relation, options ); // Also pushes the new Relation into `model._relations`
+ }
+ else {
+ Backbone.Relational.showWarnings && typeof console !== 'undefined' && console.warn( 'Relation=%o; missing or invalid relation type!', relation );
+ }
+ },
+
+ /**
+ * Add a scope for `getObjectByName` to look for model types by name.
+ * @param {Object} scope
+ */
+ addModelScope: function( scope ) {
+ this._modelScopes.push( scope );
+ },
+
+ /**
+ * Remove a scope.
+ * @param {Object} scope
+ */
+ removeModelScope: function( scope ) {
+ this._modelScopes = _.without( this._modelScopes, scope );
+ },
+
+ /**
+ * Add a set of subModelTypes to the store, that can be used to resolve the '_superModel'
+ * for a model later in 'setupSuperModel'.
+ *
+ * @param {Backbone.RelationalModel} subModelTypes
+ * @param {Backbone.RelationalModel} superModelType
+ */
+ addSubModels: function( subModelTypes, superModelType ) {
+ this._subModels.push({
+ 'superModelType': superModelType,
+ 'subModels': subModelTypes
+ });
+ },
+
+ /**
+ * Check if the given modelType is registered as another model's subModel. If so, add it to the super model's
+ * '_subModels', and set the modelType's '_superModel', '_subModelTypeName', and '_subModelTypeAttribute'.
+ *
+ * @param {Backbone.RelationalModel} modelType
+ */
+ setupSuperModel: function( modelType ) {
+ _.find( this._subModels, function( subModelDef ) {
+ return _.find( subModelDef.subModels || [], function( subModelTypeName, typeValue ) {
+ var subModelType = this.getObjectByName( subModelTypeName );
+
+ if ( modelType === subModelType ) {
+ // Set 'modelType' as a child of the found superModel
+ subModelDef.superModelType._subModels[ typeValue ] = modelType;
+
+ // Set '_superModel', '_subModelTypeValue', and '_subModelTypeAttribute' on 'modelType'.
+ modelType._superModel = subModelDef.superModelType;
+ modelType._subModelTypeValue = typeValue;
+ modelType._subModelTypeAttribute = subModelDef.superModelType.prototype.subModelTypeAttribute;
+ return true;
+ }
+ }, this );
+ }, this );
+ },
+
+ /**
+ * Add a reverse relation. Is added to the 'relations' property on model's prototype, and to
+ * existing instances of 'model' in the store as well.
+ * @param {Object} relation
+ * @param {Backbone.RelationalModel} relation.model
+ * @param {String} relation.type
+ * @param {String} relation.key
+ * @param {String|Object} relation.relatedModel
+ */
+ addReverseRelation: function( relation ) {
+ var exists = _.any( this._reverseRelations, function( rel ) {
+ return _.all( relation || [], function( val, key ) {
+ return val === rel[ key ];
+ });
+ });
+
+ if ( !exists && relation.model && relation.type ) {
+ this._reverseRelations.push( relation );
+ this._addRelation( relation.model, relation );
+ this.retroFitRelation( relation );
+ }
+ },
+
+ /**
+ * Deposit a `relation` for which the `relatedModel` can't be resolved at the moment.
+ *
+ * @param {Object} relation
+ */
+ addOrphanRelation: function( relation ) {
+ var exists = _.any( this._orphanRelations, function( rel ) {
+ return _.all( relation || [], function( val, key ) {
+ return val === rel[ key ];
+ });
+ });
+
+ if ( !exists && relation.model && relation.type ) {
+ this._orphanRelations.push( relation );
+ }
+ },
+
+ /**
+ * Try to initialize any `_orphanRelation`s
+ */
+ processOrphanRelations: function() {
+ // Make sure to operate on a copy since we're removing while iterating
+ _.each( this._orphanRelations.slice( 0 ), function( rel ) {
+ var relatedModel = Backbone.Relational.store.getObjectByName( rel.relatedModel );
+ if ( relatedModel ) {
+ this.initializeRelation( null, rel );
+ this._orphanRelations = _.without( this._orphanRelations, rel );
+ }
+ }, this );
+ },
+
+ /**
+ *
+ * @param {Backbone.RelationalModel.constructor} type
+ * @param {Object} relation
+ * @private
+ */
+ _addRelation: function( type, relation ) {
+ if ( !type.prototype.relations ) {
+ type.prototype.relations = [];
+ }
+ type.prototype.relations.push( relation );
+
+ _.each( type._subModels || [], function( subModel ) {
+ this._addRelation( subModel, relation );
+ }, this );
+ },
+
+ /**
+ * Add a 'relation' to all existing instances of 'relation.model' in the store
+ * @param {Object} relation
+ */
+ retroFitRelation: function( relation ) {
+ var coll = this.getCollection( relation.model, false );
+ coll && coll.each( function( model ) {
+ if ( !( model instanceof relation.model ) ) {
+ return;
+ }
+
+ new relation.type( model, relation );
+ }, this );
+ },
+
+ /**
+ * Find the Store's collection for a certain type of model.
+ * @param {Backbone.RelationalModel} type
+ * @param {Boolean} [create=true] Should a collection be created if none is found?
+ * @return {Backbone.Collection} A collection if found (or applicable for 'model'), or null
+ */
+ getCollection: function( type, create ) {
+ if ( type instanceof Backbone.RelationalModel ) {
+ type = type.constructor;
+ }
+
+ var rootModel = type;
+ while ( rootModel._superModel ) {
+ rootModel = rootModel._superModel;
+ }
+
+ var coll = _.findWhere( this._collections, { model: rootModel } );
+
+ if ( !coll && create !== false ) {
+ coll = this._createCollection( rootModel );
+ }
+
+ return coll;
+ },
+
+ /**
+ * Find a model type on one of the modelScopes by name. Names are split on dots.
+ * @param {String} name
+ * @return {Object}
+ */
+ getObjectByName: function( name ) {
+ var parts = name.split( '.' ),
+ type = null;
+
+ _.find( this._modelScopes, function( scope ) {
+ type = _.reduce( parts || [], function( memo, val ) {
+ return memo ? memo[ val ] : undefined;
+ }, scope );
+
+ if ( type && type !== scope ) {
+ return true;
+ }
+ }, this );
+
+ return type;
+ },
+
+ _createCollection: function( type ) {
+ var coll;
+
+ // If 'type' is an instance, take its constructor
+ if ( type instanceof Backbone.RelationalModel ) {
+ type = type.constructor;
+ }
+
+ // Type should inherit from Backbone.RelationalModel.
+ if ( type.prototype instanceof Backbone.RelationalModel ) {
+ coll = new Backbone.Collection();
+ coll.model = type;
+
+ this._collections.push( coll );
+ }
+
+ return coll;
+ },
+
+ /**
+ * Find the attribute that is to be used as the `id` on a given object
+ * @param type
+ * @param {String|Number|Object|Backbone.RelationalModel} item
+ * @return {String|Number}
+ */
+ resolveIdForItem: function( type, item ) {
+ var id = _.isString( item ) || _.isNumber( item ) ? item : null;
+
+ if ( id === null ) {
+ if ( item instanceof Backbone.RelationalModel ) {
+ id = item.id;
+ }
+ else if ( _.isObject( item ) ) {
+ id = item[ type.prototype.idAttribute ];
+ }
+ }
+
+ // Make all falsy values `null` (except for 0, which could be an id.. see '/issues/179')
+ if ( !id && id !== 0 ) {
+ id = null;
+ }
+
+ return id;
+ },
+
+ /**
+ * Find a specific model of a certain `type` in the store
+ * @param type
+ * @param {String|Number|Object|Backbone.RelationalModel} item
+ */
+ find: function( type, item ) {
+ var id = this.resolveIdForItem( type, item );
+ var coll = this.getCollection( type );
+
+ // Because the found object could be of any of the type's superModel
+ // types, only return it if it's actually of the type asked for.
+ if ( coll ) {
+ var obj = coll.get( id );
+
+ if ( obj instanceof type ) {
+ return obj;
+ }
+ }
+
+ return null;
+ },
+
+ /**
+ * Add a 'model' to its appropriate collection. Retain the original contents of 'model.collection'.
+ * @param {Backbone.RelationalModel} model
+ */
+ register: function( model ) {
+ var coll = this.getCollection( model );
+
+ if ( coll ) {
+ var modelColl = model.collection;
+ coll.add( model );
+ this.listenTo( model, 'destroy', this.unregister, this );
+ model.collection = modelColl;
+ }
+ },
+
+ /**
+ * Check if the given model may use the given `id`
+ * @param model
+ * @param [id]
+ */
+ checkId: function( model, id ) {
+ var coll = this.getCollection( model ),
+ duplicate = coll && coll.get( id );
+
+ if ( duplicate && model !== duplicate ) {
+ if ( Backbone.Relational.showWarnings && typeof console !== 'undefined' ) {
+ console.warn( 'Duplicate id! Old RelationalModel=%o, new RelationalModel=%o', duplicate, model );
+ }
+
+ throw new Error( "Cannot instantiate more than one Backbone.RelationalModel with the same id per type!" );
+ }
+ },
+
+ /**
+ * Explicitly update a model's id in its store collection
+ * @param {Backbone.RelationalModel} model
+ */
+ update: function( model ) {
+ var coll = this.getCollection( model );
+ // This triggers updating the lookup indices kept in a collection
+ coll._onModelEvent( 'change:' + model.idAttribute, model, coll );
+
+ // Trigger an event on model so related models (having the model's new id in their keyContents) can add it.
+ model.trigger( 'relational:change:id', model, coll );
+ },
+
+ /**
+ * Remove a 'model' from the store.
+ * @param {Backbone.RelationalModel} model
+ */
+ unregister: function( model ) {
+ this.stopListening( model, 'destroy', this.unregister );
+ var coll = this.getCollection( model );
+ coll && coll.remove( model );
+ },
+
+ /**
+ * Reset the `store` to it's original state. The `reverseRelations` are kept though, since attempting to
+ * re-initialize these on models would lead to a large amount of warnings.
+ */
+ reset: function() {
+ this.stopListening();
+ this._collections = [];
+ this._subModels = [];
+ this._modelScopes = [ exports ];
+ }
+ });
+ Backbone.Relational.store = new Backbone.Store();
+
+ /**
+ * The main Relation class, from which 'HasOne' and 'HasMany' inherit. Internally, 'relational:<key>' events
+ * are used to regulate addition and removal of models from relations.
+ *
+ * @param {Backbone.RelationalModel} [instance] Model that this relation is created for. If no model is supplied,
+ * Relation just tries to instantiate it's `reverseRelation` if specified, and bails out after that.
+ * @param {Object} options
+ * @param {string} options.key
+ * @param {Backbone.RelationalModel.constructor} options.relatedModel
+ * @param {Boolean|String} [options.includeInJSON=true] Serialize the given attribute for related model(s)' in toJSON, or just their ids.
+ * @param {Boolean} [options.createModels=true] Create objects from the contents of keys if the object is not found in Backbone.store.
+ * @param {Object} [options.reverseRelation] Specify a bi-directional relation. If provided, Relation will reciprocate
+ * the relation to the 'relatedModel'. Required and optional properties match 'options', except that it also needs
+ * {Backbone.Relation|String} type ('HasOne' or 'HasMany').
+ * @param {Object} opts
+ */
+ Backbone.Relation = function( instance, options, opts ) {
+ this.instance = instance;
+ // Make sure 'options' is sane, and fill with defaults from subclasses and this object's prototype
+ options = _.isObject( options ) ? options : {};
+ this.reverseRelation = _.defaults( options.reverseRelation || {}, this.options.reverseRelation );
+ this.options = _.defaults( options, this.options, Backbone.Relation.prototype.options );
+
+ this.reverseRelation.type = !_.isString( this.reverseRelation.type ) ? this.reverseRelation.type :
+ Backbone[ this.reverseRelation.type ] || Backbone.Relational.store.getObjectByName( this.reverseRelation.type );
+
+ this.key = this.options.key;
+ this.keySource = this.options.keySource || this.key;
+ this.keyDestination = this.options.keyDestination || this.keySource || this.key;
+
+ this.model = this.options.model || this.instance.constructor;
+ this.relatedModel = this.options.relatedModel;
+ if ( _.isString( this.relatedModel ) ) {
+ this.relatedModel = Backbone.Relational.store.getObjectByName( this.relatedModel );
+ }
+
+ if ( !this.checkPreconditions() ) {
+ return;
+ }
+
+ // Add the reverse relation on 'relatedModel' to the store's reverseRelations
+ if ( !this.options.isAutoRelation && this.reverseRelation.type && this.reverseRelation.key ) {
+ Backbone.Relational.store.addReverseRelation( _.defaults( {
+ isAutoRelation: true,
+ model: this.relatedModel,
+ relatedModel: this.model,
+ reverseRelation: this.options // current relation is the 'reverseRelation' for its own reverseRelation
+ },
+ this.reverseRelation // Take further properties from this.reverseRelation (type, key, etc.)
+ ) );
+ }
+
+ if ( instance ) {
+ var contentKey = this.keySource;
+ if ( contentKey !== this.key && typeof this.instance.get( this.key ) === 'object' ) {
+ contentKey = this.key;
+ }
+
+ this.setKeyContents( this.instance.get( contentKey ) );
+ this.relatedCollection = Backbone.Relational.store.getCollection( this.relatedModel );
+
+ // Explicitly clear 'keySource', to prevent a leaky abstraction if 'keySource' differs from 'key'.
+ if ( this.keySource !== this.key ) {
+ this.instance.unset( this.keySource, { silent: true } );
+ }
+
+ // Add this Relation to instance._relations
+ this.instance._relations[ this.key ] = this;
+
+ this.initialize( opts );
+
+ if ( this.options.autoFetch ) {
+ this.instance.fetchRelated( this.key, _.isObject( this.options.autoFetch ) ? this.options.autoFetch : {} );
+ }
+
+ // When 'relatedModel' are created or destroyed, check if it affects this relation.
+ this.listenTo( this.instance, 'destroy', this.destroy )
+ .listenTo( this.relatedCollection, 'relational:add relational:change:id', this.tryAddRelated )
+ .listenTo( this.relatedCollection, 'relational:remove', this.removeRelated )
+ }
+ };
+ // Fix inheritance :\
+ Backbone.Relation.extend = Backbone.Model.extend;
+ // Set up all inheritable **Backbone.Relation** properties and methods.
+ _.extend( Backbone.Relation.prototype, Backbone.Events, Backbone.Semaphore, {
+ options: {
+ createModels: true,
+ includeInJSON: true,
+ isAutoRelation: false,
+ autoFetch: false,
+ parse: false
+ },
+
+ instance: null,
+ key: null,
+ keyContents: null,
+ relatedModel: null,
+ relatedCollection: null,
+ reverseRelation: null,
+ related: null,
+
+ /**
+ * Check several pre-conditions.
+ * @return {Boolean} True if pre-conditions are satisfied, false if they're not.
+ */
+ checkPreconditions: function() {
+ var i = this.instance,
+ k = this.key,
+ m = this.model,
+ rm = this.relatedModel,
+ warn = Backbone.Relational.showWarnings && typeof console !== 'undefined';
+
+ if ( !m || !k || !rm ) {
+ warn && console.warn( 'Relation=%o: missing model, key or relatedModel (%o, %o, %o).', this, m, k, rm );
+ return false;
+ }
+ // Check if the type in 'model' inherits from Backbone.RelationalModel
+ if ( !( m.prototype instanceof Backbone.RelationalModel ) ) {
+ warn && console.warn( 'Relation=%o: model does not inherit from Backbone.RelationalModel (%o).', this, i );
+ return false;
+ }
+ // Check if the type in 'relatedModel' inherits from Backbone.RelationalModel
+ if ( !( rm.prototype instanceof Backbone.RelationalModel ) ) {
+ warn && console.warn( 'Relation=%o: relatedModel does not inherit from Backbone.RelationalModel (%o).', this, rm );
+ return false;
+ }
+ // Check if this is not a HasMany, and the reverse relation is HasMany as well
+ if ( this instanceof Backbone.HasMany && this.reverseRelation.type === Backbone.HasMany ) {
+ warn && console.warn( 'Relation=%o: relation is a HasMany, and the reverseRelation is HasMany as well.', this );
+ return false;
+ }
+ // Check if we're not attempting to create a relationship on a `key` that's already used.
+ if ( i && _.keys( i._relations ).length ) {
+ var existing = _.find( i._relations, function( rel ) {
+ return rel.key === k;
+ }, this );
+
+ if ( existing ) {
+ warn && console.warn( 'Cannot create relation=%o on %o for model=%o: already taken by relation=%o.',
+ this, k, i, existing );
+ return false;
+ }
+ }
+
+ return true;
+ },
+
+ /**
+ * Set the related model(s) for this relation
+ * @param {Backbone.Model|Backbone.Collection} related
+ */
+ setRelated: function( related ) {
+ this.related = related;
+
+ this.instance.acquire();
+ this.instance.attributes[ this.key ] = related;
+ this.instance.release();
+ },
+
+ /**
+ * Determine if a relation (on a different RelationalModel) is the reverse
+ * relation of the current one.
+ * @param {Backbone.Relation} relation
+ * @return {Boolean}
+ */
+ _isReverseRelation: function( relation ) {
+ return relation.instance instanceof this.relatedModel && this.reverseRelation.key === relation.key &&
+ this.key === relation.reverseRelation.key;
+ },
+
+ /**
+ * Get the reverse relations (pointing back to 'this.key' on 'this.instance') for the currently related model(s).
+ * @param {Backbone.RelationalModel} [model] Get the reverse relations for a specific model.
+ * If not specified, 'this.related' is used.
+ * @return {Backbone.Relation[]}
+ */
+ getReverseRelations: function( model ) {
+ var reverseRelations = [];
+ // Iterate over 'model', 'this.related.models' (if this.related is a Backbone.Collection), or wrap 'this.related' in an array.
+ var models = !_.isUndefined( model ) ? [ model ] : this.related && ( this.related.models || [ this.related ] );
+ _.each( models || [], function( related ) {
+ _.each( related.getRelations() || [], function( relation ) {
+ if ( this._isReverseRelation( relation ) ) {
+ reverseRelations.push( relation );
+ }
+ }, this );
+ }, this );
+
+ return reverseRelations;
+ },
+
+ /**
+ * When `this.instance` is destroyed, cleanup our relations.
+ * Get reverse relation, call removeRelated on each.
+ */
+ destroy: function() {
+ this.stopListening();
+
+ if ( this instanceof Backbone.HasOne ) {
+ this.setRelated( null );
+ }
+ else if ( this instanceof Backbone.HasMany ) {
+ this.setRelated( this._prepareCollection() );
+ }
+
+ _.each( this.getReverseRelations(), function( relation ) {
+ relation.removeRelated( this.instance );
+ }, this );
+ }
+ });
+
+ Backbone.HasOne = Backbone.Relation.extend({
+ options: {
+ reverseRelation: { type: 'HasMany' }
+ },
+
+ initialize: function( opts ) {
+ this.listenTo( this.instance, 'relational:change:' + this.key, this.onChange );
+
+ var related = this.findRelated( opts );
+ this.setRelated( related );
+
+ // Notify new 'related' object of the new relation.
+ _.each( this.getReverseRelations(), function( relation ) {
+ relation.addRelated( this.instance, opts );
+ }, this );
+ },
+
+ /**
+ * Find related Models.
+ * @param {Object} [options]
+ * @return {Backbone.Model}
+ */
+ findRelated: function( options ) {
+ var related = null;
+
+ options = _.defaults( { parse: this.options.parse }, options );
+
+ if ( this.keyContents instanceof this.relatedModel ) {
+ related = this.keyContents;
+ }
+ else if ( this.keyContents || this.keyContents === 0 ) { // since 0 can be a valid `id` as well
+ var opts = _.defaults( { create: this.options.createModels }, options );
+ related = this.relatedModel.findOrCreate( this.keyContents, opts );
+ }
+
+ // Nullify `keyId` if we have a related model; in case it was already part of the relation
+ if ( this.related ) {
+ this.keyId = null;
+ }
+
+ return related;
+ },
+
+ /**
+ * Normalize and reduce `keyContents` to an `id`, for easier comparison
+ * @param {String|Number|Backbone.Model} keyContents
+ */
+ setKeyContents: function( keyContents ) {
+ this.keyContents = keyContents;
+ this.keyId = Backbone.Relational.store.resolveIdForItem( this.relatedModel, this.keyContents );
+ },
+
+ /**
+ * Event handler for `change:<key>`.
+ * If the key is changed, notify old & new reverse relations and initialize the new relation.
+ */
+ onChange: function( model, attr, options ) {
+ // Don't accept recursive calls to onChange (like onChange->findRelated->findOrCreate->initializeRelations->addRelated->onChange)
+ if ( this.isLocked() ) {
+ return;
+ }
+ this.acquire();
+ options = options ? _.clone( options ) : {};
+
+ // 'options.__related' is set by 'addRelated'/'removeRelated'. If it is set, the change
+ // is the result of a call from a relation. If it's not, the change is the result of
+ // a 'set' call on this.instance.
+ var changed = _.isUndefined( options.__related ),
+ oldRelated = changed ? this.related : options.__related;
+
+ if ( changed ) {
+ this.setKeyContents( attr );
+ var related = this.findRelated( options );
+ this.setRelated( related );
+ }
+
+ // Notify old 'related' object of the terminated relation
+ if ( oldRelated && this.related !== oldRelated ) {
+ _.each( this.getReverseRelations( oldRelated ), function( relation ) {
+ relation.removeRelated( this.instance, null, options );
+ }, this );
+ }
+
+ // Notify new 'related' object of the new relation. Note we do re-apply even if this.related is oldRelated;
+ // that can be necessary for bi-directional relations if 'this.instance' was created after 'this.related'.
+ // In that case, 'this.instance' will already know 'this.related', but the reverse might not exist yet.
+ _.each( this.getReverseRelations(), function( relation ) {
+ relation.addRelated( this.instance, options );
+ }, this );
+
+ // Fire the 'change:<key>' event if 'related' was updated
+ if ( !options.silent && this.related !== oldRelated ) {
+ var dit = this;
+ this.changed = true;
+ Backbone.Relational.eventQueue.add( function() {
+ dit.instance.trigger( 'change:' + dit.key, dit.instance, dit.related, options, true );
+ dit.changed = false;
+ });
+ }
+ this.release();
+ },
+
+ /**
+ * If a new 'this.relatedModel' appears in the 'store', try to match it to the last set 'keyContents'
+ */
+ tryAddRelated: function( model, coll, options ) {
+ if ( ( this.keyId || this.keyId === 0 ) && model.id === this.keyId ) { // since 0 can be a valid `id` as well
+ this.addRelated( model, options );
+ this.keyId = null;
+ }
+ },
+
+ addRelated: function( model, options ) {
+ // Allow 'model' to set up its relations before proceeding.
+ // (which can result in a call to 'addRelated' from a relation of 'model')
+ var dit = this;
+ model.queue( function() {
+ if ( model !== dit.related ) {
+ var oldRelated = dit.related || null;
+ dit.setRelated( model );
+ dit.onChange( dit.instance, model, _.defaults( { __related: oldRelated }, options ) );
+ }
+ });
+ },
+
+ removeRelated: function( model, coll, options ) {
+ if ( !this.related ) {
+ return;
+ }
+
+ if ( model === this.related ) {
+ var oldRelated = this.related || null;
+ this.setRelated( null );
+ this.onChange( this.instance, model, _.defaults( { __related: oldRelated }, options ) );
+ }
+ }
+ });
+
+ Backbone.HasMany = Backbone.Relation.extend({
+ collectionType: null,
+
+ options: {
+ reverseRelation: { type: 'HasOne' },
+ collectionType: Backbone.Collection,
+ collectionKey: true,
+ collectionOptions: {}
+ },
+
+ initialize: function( opts ) {
+ this.listenTo( this.instance, 'relational:change:' + this.key, this.onChange );
+
+ // Handle a custom 'collectionType'
+ this.collectionType = this.options.collectionType;
+ if ( _.isString( this.collectionType ) ) {
+ this.collectionType = Backbone.Relational.store.getObjectByName( this.collectionType );
+ }
+ if ( this.collectionType !== Backbone.Collection && !( this.collectionType.prototype instanceof Backbone.Collection ) ) {
+ throw new Error( '`collectionType` must inherit from Backbone.Collection' );
+ }
+
+ var related = this.findRelated( opts );
+ this.setRelated( related );
+ },
+
+ /**
+ * Bind events and setup collectionKeys for a collection that is to be used as the backing store for a HasMany.
+ * If no 'collection' is supplied, a new collection will be created of the specified 'collectionType' option.
+ * @param {Backbone.Collection} [collection]
+ * @return {Backbone.Collection}
+ */
+ _prepareCollection: function( collection ) {
+ if ( this.related ) {
+ this.stopListening( this.related );
+ }
+
+ if ( !collection || !( collection instanceof Backbone.Collection ) ) {
+ var options = _.isFunction( this.options.collectionOptions ) ?
+ this.options.collectionOptions( this.instance ) : this.options.collectionOptions;
+
+ collection = new this.collectionType( null, options );
+ }
+
+ collection.model = this.relatedModel;
+
+ if ( this.options.collectionKey ) {
+ var key = this.options.collectionKey === true ? this.options.reverseRelation.key : this.options.collectionKey;
+
+ if ( collection[ key ] && collection[ key ] !== this.instance ) {
+ if ( Backbone.Relational.showWarnings && typeof console !== 'undefined' ) {
+ console.warn( 'Relation=%o; collectionKey=%s already exists on collection=%o', this, key, this.options.collectionKey );
+ }
+ }
+ else if ( key ) {
+ collection[ key ] = this.instance;
+ }
+ }
+
+ this.listenTo( collection, 'relational:add', this.handleAddition )
+ .listenTo( collection, 'relational:remove', this.handleRemoval )
+ .listenTo( collection, 'relational:reset', this.handleReset );
+
+ return collection;
+ },
+
+ /**
+ * Find related Models.
+ * @param {Object} [options]
+ * @return {Backbone.Collection}
+ */
+ findRelated: function( options ) {
+ var related = null;
+
+ options = _.defaults( { parse: this.options.parse }, options );
+
+ // Replace 'this.related' by 'this.keyContents' if it is a Backbone.Collection
+ if ( this.keyContents instanceof Backbone.Collection ) {
+ this._prepareCollection( this.keyContents );
+ related = this.keyContents;
+ }
+ // Otherwise, 'this.keyContents' should be an array of related object ids.
+ // Re-use the current 'this.related' if it is a Backbone.Collection; otherwise, create a new collection.
+ else {
+ var toAdd = [];
+
+ _.each( this.keyContents, function( attributes ) {
+ if ( attributes instanceof this.relatedModel ) {
+ var model = attributes;
+ }
+ else {
+ // If `merge` is true, update models here, instead of during update.
+ model = this.relatedModel.findOrCreate( attributes,
+ _.extend( { merge: true }, options, { create: this.options.createModels } )
+ );
+ }
+
+ model && toAdd.push( model );
+ }, this );
+
+ if ( this.related instanceof Backbone.Collection ) {
+ related = this.related;
+ }
+ else {
+ related = this._prepareCollection();
+ }
+
+ // By now, both `merge` and `parse` will already have been executed for models if they were specified.
+ // Disable them to prevent additional calls.
+ related.set( toAdd, _.defaults( { merge: false, parse: false }, options ) );
+ }
+
+ // Remove entries from `keyIds` that were already part of the relation (and are thus 'unchanged')
+ this.keyIds = _.difference( this.keyIds, _.pluck( related.models, 'id' ) );
+
+ return related;
+ },
+
+ /**
+ * Normalize and reduce `keyContents` to a list of `ids`, for easier comparison
+ * @param {String|Number|String[]|Number[]|Backbone.Collection} keyContents
+ */
+ setKeyContents: function( keyContents ) {
+ this.keyContents = keyContents instanceof Backbone.Collection ? keyContents : null;
+ this.keyIds = [];
+
+ if ( !this.keyContents && ( keyContents || keyContents === 0 ) ) { // since 0 can be a valid `id` as well
+ // Handle cases the an API/user supplies just an Object/id instead of an Array
+ this.keyContents = _.isArray( keyContents ) ? keyContents : [ keyContents ];
+
+ _.each( this.keyContents, function( item ) {
+ var itemId = Backbone.Relational.store.resolveIdForItem( this.relatedModel, item );
+ if ( itemId || itemId === 0 ) {
+ this.keyIds.push( itemId );
+ }
+ }, this );
+ }
+ },
+
+ /**
+ * Event handler for `change:<key>`.
+ * If the contents of the key are changed, notify old & new reverse relations and initialize the new relation.
+ */
+ onChange: function( model, attr, options ) {
+ options = options ? _.clone( options ) : {};
+ this.setKeyContents( attr );
+ this.changed = false;
+
+ var related = this.findRelated( options );
+ this.setRelated( related );
+
+ if ( !options.silent ) {
+ var dit = this;
+ Backbone.Relational.eventQueue.add( function() {
+ // The `changed` flag can be set in `handleAddition` or `handleRemoval`
+ if ( dit.changed ) {
+ dit.instance.trigger( 'change:' + dit.key, dit.instance, dit.related, options, true );
+ dit.changed = false;
+ }
+ });
+ }
+ },
+
+ /**
+ * When a model is added to a 'HasMany', trigger 'add' on 'this.instance' and notify reverse relations.
+ * (should be 'HasOne', must set 'this.instance' as their related).
+ */
+ handleAddition: function( model, coll, options ) {
+ //console.debug('handleAddition called; args=%o', arguments);
+ options = options ? _.clone( options ) : {};
+ this.changed = true;
+
+ _.each( this.getReverseRelations( model ), function( relation ) {
+ relation.addRelated( this.instance, options );
+ }, this );
+
+ // Only trigger 'add' once the newly added model is initialized (so, has its relations set up)
+ var dit = this;
+ !options.silent && Backbone.Relational.eventQueue.add( function() {
+ dit.instance.trigger( 'add:' + dit.key, model, dit.related, options );
+ });
+ },
+
+ /**
+ * When a model is removed from a 'HasMany', trigger 'remove' on 'this.instance' and notify reverse relations.
+ * (should be 'HasOne', which should be nullified)
+ */
+ handleRemoval: function( model, coll, options ) {
+ //console.debug('handleRemoval called; args=%o', arguments);
+ options = options ? _.clone( options ) : {};
+ this.changed = true;
+
+ _.each( this.getReverseRelations( model ), function( relation ) {
+ relation.removeRelated( this.instance, null, options );
+ }, this );
+
+ var dit = this;
+ !options.silent && Backbone.Relational.eventQueue.add( function() {
+ dit.instance.trigger( 'remove:' + dit.key, model, dit.related, options );
+ });
+ },
+
+ handleReset: function( coll, options ) {
+ var dit = this;
+ options = options ? _.clone( options ) : {};
+ !options.silent && Backbone.Relational.eventQueue.add( function() {
+ dit.instance.trigger( 'reset:' + dit.key, dit.related, options );
+ });
+ },
+
+ tryAddRelated: function( model, coll, options ) {
+ var item = _.contains( this.keyIds, model.id );
+
+ if ( item ) {
+ this.addRelated( model, options );
+ this.keyIds = _.without( this.keyIds, model.id );
+ }
+ },
+
+ addRelated: function( model, options ) {
+ // Allow 'model' to set up its relations before proceeding.
+ // (which can result in a call to 'addRelated' from a relation of 'model')
+ var dit = this;
+ model.queue( function() {
+ if ( dit.related && !dit.related.get( model ) ) {
+ dit.related.add( model, _.defaults( { parse: false }, options ) );
+ }
+ });
+ },
+
+ removeRelated: function( model, coll, options ) {
+ if ( this.related.get( model ) ) {
+ this.related.remove( model, options );
+ }
+ }
+ });
+
+ /**
+ * A type of Backbone.Model that also maintains relations to other models and collections.
+ * New events when compared to the original:
+ * - 'add:<key>' (model, related collection, options)
+ * - 'remove:<key>' (model, related collection, options)
+ * - 'change:<key>' (model, related model or collection, options)
+ */
+ Backbone.RelationalModel = Backbone.Model.extend({
+ relations: null, // Relation descriptions on the prototype
+ _relations: null, // Relation instances
+ _isInitialized: false,
+ _deferProcessing: false,
+ _queue: null,
+
+ subModelTypeAttribute: 'type',
+ subModelTypes: null,
+
+ constructor: function( attributes, options ) {
+ // Nasty hack, for cases like 'model.get( <HasMany key> ).add( item )'.
+ // Defer 'processQueue', so that when 'Relation.createModels' is used we trigger 'HasMany'
+ // collection events only after the model is really fully set up.
+ // Example: event for "p.on( 'add:jobs' )" -> "p.get('jobs').add( { company: c.id, person: p.id } )".
+ if ( options && options.collection ) {
+ var dit = this,
+ collection = this.collection = options.collection;
+
+ // Prevent `collection` from cascading down to nested models; they shouldn't go into this `if` clause.
+ delete options.collection;
+
+ this._deferProcessing = true;
+
+ var processQueue = function( model ) {
+ if ( model === dit ) {
+ dit._deferProcessing = false;
+ dit.processQueue();
+ collection.off( 'relational:add', processQueue );
+ }
+ };
+ collection.on( 'relational:add', processQueue );
+
+ // So we do process the queue eventually, regardless of whether this model actually gets added to 'options.collection'.
+ _.defer( function() {
+ processQueue( dit );
+ });
+ }
+
+ Backbone.Relational.store.processOrphanRelations();
+
+ this._queue = new Backbone.BlockingQueue();
+ this._queue.block();
+ Backbone.Relational.eventQueue.block();
+
+ try {
+ Backbone.Model.apply( this, arguments );
+ }
+ finally {
+ // Try to run the global queue holding external events
+ Backbone.Relational.eventQueue.unblock();
+ }
+ },
+
+ /**
+ * Override 'trigger' to queue 'change' and 'change:*' events
+ */
+ trigger: function( eventName ) {
+ if ( eventName.length > 5 && eventName.indexOf( 'change' ) === 0 ) {
+ var dit = this,
+ args = arguments;
+
+ Backbone.Relational.eventQueue.add( function() {
+ if ( !dit._isInitialized ) {
+ return;
+ }
+
+ // Determine if the `change` event is still valid, now that all relations are populated
+ var changed = true;
+ if ( eventName === 'change' ) {
+ changed = dit.hasChanged();
+ }
+ else {
+ var attr = eventName.slice( 7 ),
+ rel = dit.getRelation( attr );
+
+ if ( rel ) {
+ // If `attr` is a relation, `change:attr` get triggered from `Relation.onChange`.
+ // These take precedence over `change:attr` events triggered by `Model.set`.
+ // The relation set a fourth attribute to `true`. If this attribute is present,
+ // continue triggering this event; otherwise, it's from `Model.set` and should be stopped.
+ changed = ( args[ 4 ] === true );
+
+ // If this event was triggered by a relation, set the right value in `this.changed`
+ // (a Collection or Model instead of raw data).
+ if ( changed ) {
+ dit.changed[ attr ] = args[ 2 ];
+ }
+ // Otherwise, this event is from `Model.set`. If the relation doesn't report a change,
+ // remove attr from `dit.changed` so `hasChanged` doesn't take it into account.
+ else if ( !rel.changed ) {
+ delete dit.changed[ attr ];
+ }
+ }
+ }
+
+ changed && Backbone.Model.prototype.trigger.apply( dit, args );
+ });
+ }
+ else {
+ Backbone.Model.prototype.trigger.apply( this, arguments );
+ }
+
+ return this;
+ },
+
+ /**
+ * Initialize Relations present in this.relations; determine the type (HasOne/HasMany), then creates a new instance.
+ * Invoked in the first call so 'set' (which is made from the Backbone.Model constructor).
+ */
+ initializeRelations: function( options ) {
+ this.acquire(); // Setting up relations often also involve calls to 'set', and we only want to enter this function once
+ this._relations = {};
+
+ _.each( this.relations || [], function( rel ) {
+ Backbone.Relational.store.initializeRelation( this, rel, options );
+ }, this );
+
+ this._isInitialized = true;
+ this.release();
+ this.processQueue();
+ },
+
+ /**
+ * When new values are set, notify this model's relations (also if options.silent is set).
+ * (Relation.setRelated locks this model before calling 'set' on it to prevent loops)
+ */
+ updateRelations: function( options ) {
+ if ( this._isInitialized && !this.isLocked() ) {
+ _.each( this._relations, function( rel ) {
+ // Update from data in `rel.keySource` if set, or `rel.key` otherwise
+ var val = this.attributes[ rel.keySource ] || this.attributes[ rel.key ];
+ if ( rel.related !== val ) {
+ this.trigger( 'relational:change:' + rel.key, this, val, options || {} );
+ }
+ }, this );
+ }
+ },
+
+ /**
+ * Either add to the queue (if we're not initialized yet), or execute right away.
+ */
+ queue: function( func ) {
+ this._queue.add( func );
+ },
+
+ /**
+ * Process _queue
+ */
+ processQueue: function() {
+ if ( this._isInitialized && !this._deferProcessing && this._queue.isBlocked() ) {
+ this._queue.unblock();
+ }
+ },
+
+ /**
+ * Get a specific relation.
+ * @param key {string} The relation key to look for.
+ * @return {Backbone.Relation} An instance of 'Backbone.Relation', if a relation was found for 'key', or null.
+ */
+ getRelation: function( key ) {
+ return this._relations[ key ];
+ },
+
+ /**
+ * Get all of the created relations.
+ * @return {Backbone.Relation[]}
+ */
+ getRelations: function() {
+ return _.values( this._relations );
+ },
+
+ /**
+ * Retrieve related objects.
+ * @param key {string} The relation key to fetch models for.
+ * @param [options] {Object} Options for 'Backbone.Model.fetch' and 'Backbone.sync'.
+ * @param [refresh=false] {boolean} Fetch existing models from the server as well (in order to update them).
+ * @return {jQuery.when[]} An array of request objects
+ */
+ fetchRelated: function( key, options, refresh ) {
+ // Set default `options` for fetch
+ options = _.extend( { update: true, remove: false }, options );
+
+ var setUrl,
+ requests = [],
+ rel = this.getRelation( key ),
+ idsToFetch = rel && ( rel.keyIds || ( ( rel.keyId || rel.keyId === 0 ) ? [ rel.keyId ] : [] ) );
+
+ // On `refresh`, add the ids for current models in the relation to `idsToFetch`
+ if ( refresh ) {
+ var models = rel.related instanceof Backbone.Collection ? rel.related.models : [ rel.related ];
+ _.each( models, function( model ) {
+ if ( model.id || model.id === 0 ) {
+ idsToFetch.push( model.id );
+ }
+ });
+ }
+
+ if ( idsToFetch && idsToFetch.length ) {
+ // Find (or create) a model for each one that is to be fetched
+ var created = [],
+ models = _.map( idsToFetch, function( id ) {
+ var model = Backbone.Relational.store.find( rel.relatedModel, id );
+
+ if ( !model ) {
+ var attrs = {};
+ attrs[ rel.relatedModel.prototype.idAttribute ] = id;
+ model = rel.relatedModel.findOrCreate( attrs, options );
+ created.push( model );
+ }
+
+ return model;
+ }, this );
+
+ // Try if the 'collection' can provide a url to fetch a set of models in one request.
+ if ( rel.related instanceof Backbone.Collection && _.isFunction( rel.related.url ) ) {
+ setUrl = rel.related.url( models );
+ }
+
+ // An assumption is that when 'Backbone.Collection.url' is a function, it can handle building of set urls.
+ // To make sure it can, test if the url we got by supplying a list of models to fetch is different from
+ // the one supplied for the default fetch action (without args to 'url').
+ if ( setUrl && setUrl !== rel.related.url() ) {
+ var opts = _.defaults(
+ {
+ error: function() {
+ var args = arguments;
+ _.each( created, function( model ) {
+ model.trigger( 'destroy', model, model.collection, options );
+ options.error && options.error.apply( model, args );
+ });
+ },
+ url: setUrl
+ },
+ options
+ );
+
+ requests = [ rel.related.fetch( opts ) ];
+ }
+ else {
+ requests = _.map( models, function( model ) {
+ var opts = _.defaults(
+ {
+ error: function() {
+ if ( _.contains( created, model ) ) {
+ model.trigger( 'destroy', model, model.collection, options );
+ options.error && options.error.apply( model, arguments );
+ }
+ }
+ },
+ options
+ );
+ return model.fetch( opts );
+ }, this );
+ }
+ }
+
+ return requests;
+ },
+
+ get: function( attr ) {
+ var originalResult = Backbone.Model.prototype.get.call( this, attr );
+
+ // Use `originalResult` get if dotNotation not enabled or not required because no dot is in `attr`
+ if ( !this.dotNotation || attr.indexOf( '.' ) === -1 ) {
+ return originalResult;
+ }
+
+ // Go through all splits and return the final result
+ var splits = attr.split( '.' );
+ var result = _.reduce(splits, function( model, split ) {
+ if ( !( model instanceof Backbone.Model ) ) {
+ throw new Error( 'Attribute must be an instanceof Backbone.Model. Is: ' + model + ', currentSplit: ' + split );
+ }
+
+ return Backbone.Model.prototype.get.call( model, split );
+ }, this );
+
+ if ( originalResult !== undefined && result !== undefined ) {
+ throw new Error( "Ambiguous result for '" + attr + "'. direct result: " + originalResult + ", dotNotation: " + result );
+ }
+
+ return originalResult || result;
+ },
+
+ set: function( key, value, options ) {
+ Backbone.Relational.eventQueue.block();
+
+ // Duplicate backbone's behavior to allow separate key/value parameters, instead of a single 'attributes' object
+ var attributes;
+ if ( _.isObject( key ) || key == null ) {
+ attributes = key;
+ options = value;
+ }
+ else {
+ attributes = {};
+ attributes[ key ] = value;
+ }
+
+ try {
+ var id = this.id,
+ newId = attributes && this.idAttribute in attributes && attributes[ this.idAttribute ];
+
+ // Check if we're not setting a duplicate id before actually calling `set`.
+ Backbone.Relational.store.checkId( this, newId );
+
+ var result = Backbone.Model.prototype.set.apply( this, arguments );
+
+ // Ideal place to set up relations, if this is the first time we're here for this model
+ if ( !this._isInitialized && !this.isLocked() ) {
+ this.constructor.initializeModelHierarchy();
+ Backbone.Relational.store.register( this );
+ this.initializeRelations( options );
+ }
+ // The store should know about an `id` update asap
+ else if ( newId && newId !== id ) {
+ Backbone.Relational.store.update( this );
+ }
+
+ if ( attributes ) {
+ this.updateRelations( options );
+ }
+ }
+ finally {
+ // Try to run the global queue holding external events
+ Backbone.Relational.eventQueue.unblock();
+ }
+
+ return result;
+ },
+
+ unset: function( attribute, options ) {
+ Backbone.Relational.eventQueue.block();
+
+ var result = Backbone.Model.prototype.unset.apply( this, arguments );
+ this.updateRelations( options );
+
+ // Try to run the global queue holding external events
+ Backbone.Relational.eventQueue.unblock();
+
+ return result;
+ },
+
+ clear: function( options ) {
+ Backbone.Relational.eventQueue.block();
+
+ var result = Backbone.Model.prototype.clear.apply( this, arguments );
+ this.updateRelations( options );
+
+ // Try to run the global queue holding external events
+ Backbone.Relational.eventQueue.unblock();
+
+ return result;
+ },
+
+ clone: function() {
+ var attributes = _.clone( this.attributes );
+ if ( !_.isUndefined( attributes[ this.idAttribute ] ) ) {
+ attributes[ this.idAttribute ] = null;
+ }
+
+ _.each( this.getRelations(), function( rel ) {
+ delete attributes[ rel.key ];
+ });
+
+ return new this.constructor( attributes );
+ },
+
+ /**
+ * Convert relations to JSON, omits them when required
+ */
+ toJSON: function( options ) {
+ // If this Model has already been fully serialized in this branch once, return to avoid loops
+ if ( this.isLocked() ) {
+ return this.id;
+ }
+
+ this.acquire();
+ var json = Backbone.Model.prototype.toJSON.call( this, options );
+
+ if ( this.constructor._superModel && !( this.constructor._subModelTypeAttribute in json ) ) {
+ json[ this.constructor._subModelTypeAttribute ] = this.constructor._subModelTypeValue;
+ }
+
+ _.each( this._relations, function( rel ) {
+ var related = json[ rel.key ],
+ includeInJSON = rel.options.includeInJSON,
+ value = null;
+
+ if ( includeInJSON === true ) {
+ if ( related && _.isFunction( related.toJSON ) ) {
+ value = related.toJSON( options );
+ }
+ }
+ else if ( _.isString( includeInJSON ) ) {
+ if ( related instanceof Backbone.Collection ) {
+ value = related.pluck( includeInJSON );
+ }
+ else if ( related instanceof Backbone.Model ) {
+ value = related.get( includeInJSON );
+ }
+
+ // Add ids for 'unfound' models if includeInJSON is equal to (only) the relatedModel's `idAttribute`
+ if ( includeInJSON === rel.relatedModel.prototype.idAttribute ) {
+ if ( rel instanceof Backbone.HasMany ) {
+ value = value.concat( rel.keyIds );
+ }
+ else if ( rel instanceof Backbone.HasOne ) {
+ value = value || rel.keyId;
+ }
+ }
+ }
+ else if ( _.isArray( includeInJSON ) ) {
+ if ( related instanceof Backbone.Collection ) {
+ value = [];
+ related.each( function( model ) {
+ var curJson = {};
+ _.each( includeInJSON, function( key ) {
+ curJson[ key ] = model.get( key );
+ });
+ value.push( curJson );
+ });
+ }
+ else if ( related instanceof Backbone.Model ) {
+ value = {};
+ _.each( includeInJSON, function( key ) {
+ value[ key ] = related.get( key );
+ });
+ }
+ }
+ else {
+ delete json[ rel.key ];
+ }
+
+ if ( includeInJSON ) {
+ json[ rel.keyDestination ] = value;
+ }
+
+ if ( rel.keyDestination !== rel.key ) {
+ delete json[ rel.key ];
+ }
+ });
+
+ this.release();
+ return json;
+ }
+ },
+ {
+ /**
+ *
+ * @param superModel
+ * @returns {Backbone.RelationalModel.constructor}
+ */
+ setup: function( superModel ) {
+ // We don't want to share a relations array with a parent, as this will cause problems with
+ // reverse relations.
+ this.prototype.relations = ( this.prototype.relations || [] ).slice( 0 );
+
+ this._subModels = {};
+ this._superModel = null;
+
+ // If this model has 'subModelTypes' itself, remember them in the store
+ if ( this.prototype.hasOwnProperty( 'subModelTypes' ) ) {
+ Backbone.Relational.store.addSubModels( this.prototype.subModelTypes, this );
+ }
+ // The 'subModelTypes' property should not be inherited, so reset it.
+ else {
+ this.prototype.subModelTypes = null;
+ }
+
+ // Initialize all reverseRelations that belong to this new model.
+ _.each( this.prototype.relations || [], function( rel ) {
+ if ( !rel.model ) {
+ rel.model = this;
+ }
+
+ if ( rel.reverseRelation && rel.model === this ) {
+ var preInitialize = true;
+ if ( _.isString( rel.relatedModel ) ) {
+ /**
+ * The related model might not be defined for two reasons
+ * 1. it is related to itself
+ * 2. it never gets defined, e.g. a typo
+ * 3. the model hasn't been defined yet, but will be later
+ * In neither of these cases do we need to pre-initialize reverse relations.
+ * However, for 3. (which is, to us, indistinguishable from 2.), we do need to attempt
+ * setting up this relation again later, in case the related model is defined later.
+ */
+ var relatedModel = Backbone.Relational.store.getObjectByName( rel.relatedModel );
+ preInitialize = relatedModel && ( relatedModel.prototype instanceof Backbone.RelationalModel );
+ }
+
+ if ( preInitialize ) {
+ Backbone.Relational.store.initializeRelation( null, rel );
+ }
+ else if ( _.isString( rel.relatedModel ) ) {
+ Backbone.Relational.store.addOrphanRelation( rel );
+ }
+ }
+ }, this );
+
+ return this;
+ },
+
+ /**
+ * Create a 'Backbone.Model' instance based on 'attributes'.
+ * @param {Object} attributes
+ * @param {Object} [options]
+ * @return {Backbone.Model}
+ */
+ build: function( attributes, options ) {
+ var model = this;
+
+ // 'build' is a possible entrypoint; it's possible no model hierarchy has been determined yet.
+ this.initializeModelHierarchy();
+
+ // Determine what type of (sub)model should be built if applicable.
+ // Lookup the proper subModelType in 'this._subModels'.
+ if ( this._subModels && this.prototype.subModelTypeAttribute in attributes ) {
+ var subModelTypeAttribute = attributes[ this.prototype.subModelTypeAttribute ];
+ var subModelType = this._subModels[ subModelTypeAttribute ];
+ if ( subModelType ) {
+ model = subModelType;
+ }
+ }
+
+ return new model( attributes, options );
+ },
+
+ /**
+ *
+ */
+ initializeModelHierarchy: function() {
+ // If we're here for the first time, try to determine if this modelType has a 'superModel'.
+ if ( _.isUndefined( this._superModel ) || _.isNull( this._superModel ) ) {
+ Backbone.Relational.store.setupSuperModel( this );
+
+ // If a superModel has been found, copy relations from the _superModel if they haven't been
+ // inherited automatically (due to a redefinition of 'relations').
+ // Otherwise, make sure we don't get here again for this type by making '_superModel' false so we fail
+ // the isUndefined/isNull check next time.
+ if ( this._superModel && this._superModel.prototype.relations ) {
+ // Find relations that exist on the `_superModel`, but not yet on this model.
+ var inheritedRelations = _.select( this._superModel.prototype.relations || [], function( superRel ) {
+ return !_.any( this.prototype.relations || [], function( rel ) {
+ return superRel.relatedModel === rel.relatedModel && superRel.key === rel.key;
+ }, this );
+ }, this );
+
+ this.prototype.relations = inheritedRelations.concat( this.prototype.relations );
+ }
+ else {
+ this._superModel = false;
+ }
+ }
+
+ // If we came here through 'build' for a model that has 'subModelTypes', and not all of them have been resolved yet, try to resolve each.
+ if ( this.prototype.subModelTypes && _.keys( this.prototype.subModelTypes ).length !== _.keys( this._subModels ).length ) {
+ _.each( this.prototype.subModelTypes || [], function( subModelTypeName ) {
+ var subModelType = Backbone.Relational.store.getObjectByName( subModelTypeName );
+ subModelType && subModelType.initializeModelHierarchy();
+ });
+ }
+ },
+
+ /**
+ * Find an instance of `this` type in 'Backbone.Relational.store'.
+ * - If `attributes` is a string or a number, `findOrCreate` will just query the `store` and return a model if found.
+ * - If `attributes` is an object and is found in the store, the model will be updated with `attributes` unless `options.update` is `false`.
+ * Otherwise, a new model is created with `attributes` (unless `options.create` is explicitly set to `false`).
+ * @param {Object|String|Number} attributes Either a model's id, or the attributes used to create or update a model.
+ * @param {Object} [options]
+ * @param {Boolean} [options.create=true]
+ * @param {Boolean} [options.merge=true]
+ * @param {Boolean} [options.parse=false]
+ * @return {Backbone.RelationalModel}
+ */
+ findOrCreate: function( attributes, options ) {
+ options || ( options = {} );
+ var parsedAttributes = ( _.isObject( attributes ) && options.parse && this.prototype.parse ) ?
+ this.prototype.parse( attributes ) : attributes;
+
+ // Try to find an instance of 'this' model type in the store
+ var model = Backbone.Relational.store.find( this, parsedAttributes );
+
+ // If we found an instance, update it with the data in 'item' (unless 'options.merge' is false).
+ // If not, create an instance (unless 'options.create' is false).
+ if ( _.isObject( attributes ) ) {
+ if ( model && options.merge !== false ) {
+ // Make sure `options.collection` doesn't cascade to nested models
+ delete options.collection;
+
+ model.set( parsedAttributes, options );
+ }
+ else if ( !model && options.create !== false ) {
+ model = this.build( attributes, options );
+ }
+ }
+
+ return model;
+ }
+ });
+ _.extend( Backbone.RelationalModel.prototype, Backbone.Semaphore );
+
+ /**
+ * Override Backbone.Collection._prepareModel, so objects will be built using the correct type
+ * if the collection.model has subModels.
+ * Attempts to find a model for `attrs` in Backbone.store through `findOrCreate`
+ * (which sets the new properties on it if found), or instantiates a new model.
+ */
+ Backbone.Collection.prototype.__prepareModel = Backbone.Collection.prototype._prepareModel;
+ Backbone.Collection.prototype._prepareModel = function ( attrs, options ) {
+ var model;
+
+ if ( attrs instanceof Backbone.Model ) {
+ if ( !attrs.collection ) {
+ attrs.collection = this;
+ }
+ model = attrs;
+ }
+ else {
+ options || ( options = {} );
+ options.collection = this;
+
+ if ( typeof this.model.findOrCreate !== 'undefined' ) {
+ model = this.model.findOrCreate( attrs, options );
+ }
+ else {
+ model = new this.model( attrs, options );
+ }
+
+ if ( model && model.isNew() && !model._validate( attrs, options ) ) {
+ this.trigger( 'invalid', this, attrs, options );
+ model = false;
+ }
+ }
+
+ return model;
+ };
+
+
+ /**
+ * Override Backbone.Collection.set, so we'll create objects from attributes where required,
+ * and update the existing models. Also, trigger 'relational:add'.
+ */
+ var set = Backbone.Collection.prototype.__set = Backbone.Collection.prototype.set;
+ Backbone.Collection.prototype.set = function( models, options ) {
+ // Short-circuit if this Collection doesn't hold RelationalModels
+ if ( !( this.model.prototype instanceof Backbone.RelationalModel ) ) {
+ return set.apply( this, arguments );
+ }
+
+ if ( options && options.parse ) {
+ models = this.parse( models, options );
+ }
+
+ if ( !_.isArray( models ) ) {
+ models = models ? [ models ] : [];
+ }
+
+ var newModels = [],
+ toAdd = [];
+
+ //console.debug( 'calling add on coll=%o; model=%o, options=%o', this, models, options );
+ _.each( models, function( model ) {
+ if ( !( model instanceof Backbone.Model ) ) {
+ model = Backbone.Collection.prototype._prepareModel.call( this, model, options );
+ }
+
+ if ( model ) {
+ toAdd.push( model );
+
+ if ( !( this.get( model ) || this.get( model.cid ) ) ) {
+ newModels.push( model );
+ }
+ // If we arrive in `add` while performing a `set` (after a create, so the model gains an `id`),
+ // we may get here before `_onModelEvent` has had the chance to update `_byId`.
+ else if ( model.id != null ) {
+ this._byId[ model.id ] = model;
+ }
+ }
+ }, this );
+
+ // Add 'models' in a single batch, so the original add will only be called once (and thus 'sort', etc).
+ // If `parse` was specified, the collection and contained models have been parsed now.
+ set.call( this, toAdd, _.defaults( { parse: false }, options ) );
+
+ _.each( newModels, function( model ) {
+ // Fire a `relational:add` event for any model in `newModels` that has actually been added to the collection.
+ if ( this.get( model ) || this.get( model.cid ) ) {
+ this.trigger( 'relational:add', model, this, options );
+ }
+ }, this );
+
+ return this;
+ };
+
+ /**
+ * Override 'Backbone.Collection.remove' to trigger 'relational:remove'.
+ */
+ var remove = Backbone.Collection.prototype.__remove = Backbone.Collection.prototype.remove;
+ Backbone.Collection.prototype.remove = function( models, options ) {
+ // Short-circuit if this Collection doesn't hold RelationalModels
+ if ( !( this.model.prototype instanceof Backbone.RelationalModel ) ) {
+ return remove.apply( this, arguments );
+ }
+
+ models = _.isArray( models ) ? models.slice() : [ models ];
+ options || ( options = {} );
+
+ var toRemove = [];
+
+ //console.debug('calling remove on coll=%o; models=%o, options=%o', this, models, options );
+ _.each( models, function( model ) {
+ model = this.get( model ) || this.get( model.cid );
+ model && toRemove.push( model );
+ }, this );
+
+ if ( toRemove.length ) {
+ remove.call( this, toRemove, options );
+
+ _.each( toRemove, function( model ) {
+ this.trigger('relational:remove', model, this, options);
+ }, this );
+ }
+
+ return this;
+ };
+
+ /**
+ * Override 'Backbone.Collection.reset' to trigger 'relational:reset'.
+ */
+ var reset = Backbone.Collection.prototype.__reset = Backbone.Collection.prototype.reset;
+ Backbone.Collection.prototype.reset = function( models, options ) {
+ options = _.extend( { merge: true }, options );
+ reset.call( this, models, options );
+
+ if ( this.model.prototype instanceof Backbone.RelationalModel ) {
+ this.trigger( 'relational:reset', this, options );
+ }
+
+ return this;
+ };
+
+ /**
+ * Override 'Backbone.Collection.sort' to trigger 'relational:reset'.
+ */
+ var sort = Backbone.Collection.prototype.__sort = Backbone.Collection.prototype.sort;
+ Backbone.Collection.prototype.sort = function( options ) {
+ sort.call( this, options );
+
+ if ( this.model.prototype instanceof Backbone.RelationalModel ) {
+ this.trigger( 'relational:reset', this, options );
+ }
+
+ return this;
+ };
+
+ /**
+ * Override 'Backbone.Collection.trigger' so 'add', 'remove' and 'reset' events are queued until relations
+ * are ready.
+ */
+ var trigger = Backbone.Collection.prototype.__trigger = Backbone.Collection.prototype.trigger;
+ Backbone.Collection.prototype.trigger = function( eventName ) {
+ // Short-circuit if this Collection doesn't hold RelationalModels
+ if ( !( this.model.prototype instanceof Backbone.RelationalModel ) ) {
+ return trigger.apply( this, arguments );
+ }
+
+ if ( eventName === 'add' || eventName === 'remove' || eventName === 'reset' ) {
+ var dit = this,
+ args = arguments;
+
+ if ( _.isObject( args[ 3 ] ) ) {
+ args = _.toArray( args );
+ // the fourth argument is the option object.
+ // we need to clone it, as it could be modified while we wait on the eventQueue to be unblocked
+ args[ 3 ] = _.clone( args[ 3 ] );
+ }
+
+ Backbone.Relational.eventQueue.add( function() {
+ trigger.apply( dit, args );
+ });
+ }
+ else {
+ trigger.apply( this, arguments );
+ }
+
+ return this;
+ };
+
+ // Override .extend() to automatically call .setup()
+ Backbone.RelationalModel.extend = function( protoProps, classProps ) {
+ var child = Backbone.Model.extend.apply( this, arguments );
+
+ child.setup( this );
+
+ return child;
+ };
+})();
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/web/res/js/backbone.js Fri Jun 07 11:55:46 2013 +0200
@@ -0,0 +1,4 @@
+(function(){var t=this;var e=t.Backbone;var i=[];var r=i.push;var s=i.slice;var n=i.splice;var a;if(typeof exports!=="undefined"){a=exports}else{a=t.Backbone={}}a.VERSION="1.0.0";var h=t._;if(!h&&typeof require!=="undefined")h=require("underscore");a.$=t.jQuery||t.Zepto||t.ender||t.$;a.noConflict=function(){t.Backbone=e;return this};a.emulateHTTP=false;a.emulateJSON=false;var o=a.Events={on:function(t,e,i){if(!l(this,"on",t,[e,i])||!e)return this;this._events||(this._events={});var r=this._events[t]||(this._events[t]=[]);r.push({callback:e,context:i,ctx:i||this});return this},once:function(t,e,i){if(!l(this,"once",t,[e,i])||!e)return this;var r=this;var s=h.once(function(){r.off(t,s);e.apply(this,arguments)});s._callback=e;return this.on(t,s,i)},off:function(t,e,i){var r,s,n,a,o,u,c,f;if(!this._events||!l(this,"off",t,[e,i]))return this;if(!t&&!e&&!i){this._events={};return this}a=t?[t]:h.keys(this._events);for(o=0,u=a.length;o<u;o++){t=a[o];if(n=this._events[t]){this._events[t]=r=[];if(e||i){for(c=0,f=n.length;c<f;c++){s=n[c];if(e&&e!==s.callback&&e!==s.callback._callback||i&&i!==s.context){r.push(s)}}}if(!r.length)delete this._events[t]}}return this},trigger:function(t){if(!this._events)return this;var e=s.call(arguments,1);if(!l(this,"trigger",t,e))return this;var i=this._events[t];var r=this._events.all;if(i)c(i,e);if(r)c(r,arguments);return this},stopListening:function(t,e,i){var r=this._listeners;if(!r)return this;var s=!e&&!i;if(typeof e==="object")i=this;if(t)(r={})[t._listenerId]=t;for(var n in r){r[n].off(e,i,this);if(s)delete this._listeners[n]}return this}};var u=/\s+/;var l=function(t,e,i,r){if(!i)return true;if(typeof i==="object"){for(var s in i){t[e].apply(t,[s,i[s]].concat(r))}return false}if(u.test(i)){var n=i.split(u);for(var a=0,h=n.length;a<h;a++){t[e].apply(t,[n[a]].concat(r))}return false}return true};var c=function(t,e){var i,r=-1,s=t.length,n=e[0],a=e[1],h=e[2];switch(e.length){case 0:while(++r<s)(i=t[r]).callback.call(i.ctx);return;case 1:while(++r<s)(i=t[r]).callback.call(i.ctx,n);return;case 2:while(++r<s)(i=t[r]).callback.call(i.ctx,n,a);return;case 3:while(++r<s)(i=t[r]).callback.call(i.ctx,n,a,h);return;default:while(++r<s)(i=t[r]).callback.apply(i.ctx,e)}};var f={listenTo:"on",listenToOnce:"once"};h.each(f,function(t,e){o[e]=function(e,i,r){var s=this._listeners||(this._listeners={});var n=e._listenerId||(e._listenerId=h.uniqueId("l"));s[n]=e;if(typeof i==="object")r=this;e[t](i,r,this);return this}});o.bind=o.on;o.unbind=o.off;h.extend(a,o);var d=a.Model=function(t,e){var i;var r=t||{};e||(e={});this.cid=h.uniqueId("c");this.attributes={};h.extend(this,h.pick(e,p));if(e.parse)r=this.parse(r,e)||{};if(i=h.result(this,"defaults")){r=h.defaults({},r,i)}this.set(r,e);this.changed={};this.initialize.apply(this,arguments)};var p=["url","urlRoot","collection"];h.extend(d.prototype,o,{changed:null,validationError:null,idAttribute:"id",initialize:function(){},toJSON:function(t){return h.clone(this.attributes)},sync:function(){return a.sync.apply(this,arguments)},get:function(t){return this.attributes[t]},escape:function(t){return h.escape(this.get(t))},has:function(t){return this.get(t)!=null},set:function(t,e,i){var r,s,n,a,o,u,l,c;if(t==null)return this;if(typeof t==="object"){s=t;i=e}else{(s={})[t]=e}i||(i={});if(!this._validate(s,i))return false;n=i.unset;o=i.silent;a=[];u=this._changing;this._changing=true;if(!u){this._previousAttributes=h.clone(this.attributes);this.changed={}}c=this.attributes,l=this._previousAttributes;if(this.idAttribute in s)this.id=s[this.idAttribute];for(r in s){e=s[r];if(!h.isEqual(c[r],e))a.push(r);if(!h.isEqual(l[r],e)){this.changed[r]=e}else{delete this.changed[r]}n?delete c[r]:c[r]=e}if(!o){if(a.length)this._pending=true;for(var f=0,d=a.length;f<d;f++){this.trigger("change:"+a[f],this,c[a[f]],i)}}if(u)return this;if(!o){while(this._pending){this._pending=false;this.trigger("change",this,i)}}this._pending=false;this._changing=false;return this},unset:function(t,e){return this.set(t,void 0,h.extend({},e,{unset:true}))},clear:function(t){var e={};for(var i in this.attributes)e[i]=void 0;return this.set(e,h.extend({},t,{unset:true}))},hasChanged:function(t){if(t==null)return!h.isEmpty(this.changed);return h.has(this.changed,t)},changedAttributes:function(t){if(!t)return this.hasChanged()?h.clone(this.changed):false;var e,i=false;var r=this._changing?this._previousAttributes:this.attributes;for(var s in t){if(h.isEqual(r[s],e=t[s]))continue;(i||(i={}))[s]=e}return i},previous:function(t){if(t==null||!this._previousAttributes)return null;return this._previousAttributes[t]},previousAttributes:function(){return h.clone(this._previousAttributes)},fetch:function(t){t=t?h.clone(t):{};if(t.parse===void 0)t.parse=true;var e=this;var i=t.success;t.success=function(r){if(!e.set(e.parse(r,t),t))return false;if(i)i(e,r,t);e.trigger("sync",e,r,t)};R(this,t);return this.sync("read",this,t)},save:function(t,e,i){var r,s,n,a=this.attributes;if(t==null||typeof t==="object"){r=t;i=e}else{(r={})[t]=e}if(r&&(!i||!i.wait)&&!this.set(r,i))return false;i=h.extend({validate:true},i);if(!this._validate(r,i))return false;if(r&&i.wait){this.attributes=h.extend({},a,r)}if(i.parse===void 0)i.parse=true;var o=this;var u=i.success;i.success=function(t){o.attributes=a;var e=o.parse(t,i);if(i.wait)e=h.extend(r||{},e);if(h.isObject(e)&&!o.set(e,i)){return false}if(u)u(o,t,i);o.trigger("sync",o,t,i)};R(this,i);s=this.isNew()?"create":i.patch?"patch":"update";if(s==="patch")i.attrs=r;n=this.sync(s,this,i);if(r&&i.wait)this.attributes=a;return n},destroy:function(t){t=t?h.clone(t):{};var e=this;var i=t.success;var r=function(){e.trigger("destroy",e,e.collection,t)};t.success=function(s){if(t.wait||e.isNew())r();if(i)i(e,s,t);if(!e.isNew())e.trigger("sync",e,s,t)};if(this.isNew()){t.success();return false}R(this,t);var s=this.sync("delete",this,t);if(!t.wait)r();return s},url:function(){var t=h.result(this,"urlRoot")||h.result(this.collection,"url")||U();if(this.isNew())return t;return t+(t.charAt(t.length-1)==="/"?"":"/")+encodeURIComponent(this.id)},parse:function(t,e){return t},clone:function(){return new this.constructor(this.attributes)},isNew:function(){return this.id==null},isValid:function(t){return this._validate({},h.extend(t||{},{validate:true}))},_validate:function(t,e){if(!e.validate||!this.validate)return true;t=h.extend({},this.attributes,t);var i=this.validationError=this.validate(t,e)||null;if(!i)return true;this.trigger("invalid",this,i,h.extend(e||{},{validationError:i}));return false}});var v=["keys","values","pairs","invert","pick","omit"];h.each(v,function(t){d.prototype[t]=function(){var e=s.call(arguments);e.unshift(this.attributes);return h[t].apply(h,e)}});var g=a.Collection=function(t,e){e||(e={});if(e.url)this.url=e.url;if(e.model)this.model=e.model;if(e.comparator!==void 0)this.comparator=e.comparator;this._reset();this.initialize.apply(this,arguments);if(t)this.reset(t,h.extend({silent:true},e))};var m={add:true,remove:true,merge:true};var y={add:true,merge:false,remove:false};h.extend(g.prototype,o,{model:d,initialize:function(){},toJSON:function(t){return this.map(function(e){return e.toJSON(t)})},sync:function(){return a.sync.apply(this,arguments)},add:function(t,e){return this.set(t,h.defaults(e||{},y))},remove:function(t,e){t=h.isArray(t)?t.slice():[t];e||(e={});var i,r,s,n;for(i=0,r=t.length;i<r;i++){n=this.get(t[i]);if(!n)continue;delete this._byId[n.id];delete this._byId[n.cid];s=this.indexOf(n);this.models.splice(s,1);this.length--;if(!e.silent){e.index=s;n.trigger("remove",n,this,e)}this._removeReference(n)}return this},set:function(t,e){e=h.defaults(e||{},m);if(e.parse)t=this.parse(t,e);if(!h.isArray(t))t=t?[t]:[];var i,s,a,o,u,l;var c=e.at;var f=this.comparator&&c==null&&e.sort!==false;var d=h.isString(this.comparator)?this.comparator:null;var p=[],v=[],g={};for(i=0,s=t.length;i<s;i++){if(!(a=this._prepareModel(t[i],e)))continue;if(u=this.get(a)){if(e.remove)g[u.cid]=true;if(e.merge){u.set(a.attributes,e);if(f&&!l&&u.hasChanged(d))l=true}}else if(e.add){p.push(a);a.on("all",this._onModelEvent,this);this._byId[a.cid]=a;if(a.id!=null)this._byId[a.id]=a}}if(e.remove){for(i=0,s=this.length;i<s;++i){if(!g[(a=this.models[i]).cid])v.push(a)}if(v.length)this.remove(v,e)}if(p.length){if(f)l=true;this.length+=p.length;if(c!=null){n.apply(this.models,[c,0].concat(p))}else{r.apply(this.models,p)}}if(l)this.sort({silent:true});if(e.silent)return this;for(i=0,s=p.length;i<s;i++){(a=p[i]).trigger("add",a,this,e)}if(l)this.trigger("sort",this,e);return this},reset:function(t,e){e||(e={});for(var i=0,r=this.models.length;i<r;i++){this._removeReference(this.models[i])}e.previousModels=this.models;this._reset();this.add(t,h.extend({silent:true},e));if(!e.silent)this.trigger("reset",this,e);return this},push:function(t,e){t=this._prepareModel(t,e);this.add(t,h.extend({at:this.length},e));return t},pop:function(t){var e=this.at(this.length-1);this.remove(e,t);return e},unshift:function(t,e){t=this._prepareModel(t,e);this.add(t,h.extend({at:0},e));return t},shift:function(t){var e=this.at(0);this.remove(e,t);return e},slice:function(t,e){return this.models.slice(t,e)},get:function(t){if(t==null)return void 0;return this._byId[t.id!=null?t.id:t.cid||t]},at:function(t){return this.models[t]},where:function(t,e){if(h.isEmpty(t))return e?void 0:[];return this[e?"find":"filter"](function(e){for(var i in t){if(t[i]!==e.get(i))return false}return true})},findWhere:function(t){return this.where(t,true)},sort:function(t){if(!this.comparator)throw new Error("Cannot sort a set without a comparator");t||(t={});if(h.isString(this.comparator)||this.comparator.length===1){this.models=this.sortBy(this.comparator,this)}else{this.models.sort(h.bind(this.comparator,this))}if(!t.silent)this.trigger("sort",this,t);return this},sortedIndex:function(t,e,i){e||(e=this.comparator);var r=h.isFunction(e)?e:function(t){return t.get(e)};return h.sortedIndex(this.models,t,r,i)},pluck:function(t){return h.invoke(this.models,"get",t)},fetch:function(t){t=t?h.clone(t):{};if(t.parse===void 0)t.parse=true;var e=t.success;var i=this;t.success=function(r){var s=t.reset?"reset":"set";i[s](r,t);if(e)e(i,r,t);i.trigger("sync",i,r,t)};R(this,t);return this.sync("read",this,t)},create:function(t,e){e=e?h.clone(e):{};if(!(t=this._prepareModel(t,e)))return false;if(!e.wait)this.add(t,e);var i=this;var r=e.success;e.success=function(s){if(e.wait)i.add(t,e);if(r)r(t,s,e)};t.save(null,e);return t},parse:function(t,e){return t},clone:function(){return new this.constructor(this.models)},_reset:function(){this.length=0;this.models=[];this._byId={}},_prepareModel:function(t,e){if(t instanceof d){if(!t.collection)t.collection=this;return t}e||(e={});e.collection=this;var i=new this.model(t,e);if(!i._validate(t,e)){this.trigger("invalid",this,t,e);return false}return i},_removeReference:function(t){if(this===t.collection)delete t.collection;t.off("all",this._onModelEvent,this)},_onModelEvent:function(t,e,i,r){if((t==="add"||t==="remove")&&i!==this)return;if(t==="destroy")this.remove(e,r);if(e&&t==="change:"+e.idAttribute){delete this._byId[e.previous(e.idAttribute)];if(e.id!=null)this._byId[e.id]=e}this.trigger.apply(this,arguments)}});var _=["forEach","each","map","collect","reduce","foldl","inject","reduceRight","foldr","find","detect","filter","select","reject","every","all","some","any","include","contains","invoke","max","min","toArray","size","first","head","take","initial","rest","tail","drop","last","without","indexOf","shuffle","lastIndexOf","isEmpty","chain"];h.each(_,function(t){g.prototype[t]=function(){var e=s.call(arguments);e.unshift(this.models);return h[t].apply(h,e)}});var w=["groupBy","countBy","sortBy"];h.each(w,function(t){g.prototype[t]=function(e,i){var r=h.isFunction(e)?e:function(t){return t.get(e)};return h[t](this.models,r,i)}});var b=a.View=function(t){this.cid=h.uniqueId("view");this._configure(t||{});this._ensureElement();this.initialize.apply(this,arguments);this.delegateEvents()};var x=/^(\S+)\s*(.*)$/;var E=["model","collection","el","id","attributes","className","tagName","events"];h.extend(b.prototype,o,{tagName:"div",$:function(t){return this.$el.find(t)},initialize:function(){},render:function(){return this},remove:function(){this.$el.remove();this.stopListening();return this},setElement:function(t,e){if(this.$el)this.undelegateEvents();this.$el=t instanceof a.$?t:a.$(t);this.el=this.$el[0];if(e!==false)this.delegateEvents();return this},delegateEvents:function(t){if(!(t||(t=h.result(this,"events"))))return this;this.undelegateEvents();for(var e in t){var i=t[e];if(!h.isFunction(i))i=this[t[e]];if(!i)continue;var r=e.match(x);var s=r[1],n=r[2];i=h.bind(i,this);s+=".delegateEvents"+this.cid;if(n===""){this.$el.on(s,i)}else{this.$el.on(s,n,i)}}return this},undelegateEvents:function(){this.$el.off(".delegateEvents"+this.cid);return this},_configure:function(t){if(this.options)t=h.extend({},h.result(this,"options"),t);h.extend(this,h.pick(t,E));this.options=t},_ensureElement:function(){if(!this.el){var t=h.extend({},h.result(this,"attributes"));if(this.id)t.id=h.result(this,"id");if(this.className)t["class"]=h.result(this,"className");var e=a.$("<"+h.result(this,"tagName")+">").attr(t);this.setElement(e,false)}else{this.setElement(h.result(this,"el"),false)}}});a.sync=function(t,e,i){var r=k[t];h.defaults(i||(i={}),{emulateHTTP:a.emulateHTTP,emulateJSON:a.emulateJSON});var s={type:r,dataType:"json"};if(!i.url){s.url=h.result(e,"url")||U()}if(i.data==null&&e&&(t==="create"||t==="update"||t==="patch")){s.contentType="application/json";s.data=JSON.stringify(i.attrs||e.toJSON(i))}if(i.emulateJSON){s.contentType="application/x-www-form-urlencoded";s.data=s.data?{model:s.data}:{}}if(i.emulateHTTP&&(r==="PUT"||r==="DELETE"||r==="PATCH")){s.type="POST";if(i.emulateJSON)s.data._method=r;var n=i.beforeSend;i.beforeSend=function(t){t.setRequestHeader("X-HTTP-Method-Override",r);if(n)return n.apply(this,arguments)}}if(s.type!=="GET"&&!i.emulateJSON){s.processData=false}if(s.type==="PATCH"&&window.ActiveXObject&&!(window.external&&window.external.msActiveXFilteringEnabled)){s.xhr=function(){return new ActiveXObject("Microsoft.XMLHTTP")}}var o=i.xhr=a.ajax(h.extend(s,i));e.trigger("request",e,o,i);return o};var k={create:"POST",update:"PUT",patch:"PATCH","delete":"DELETE",read:"GET"};a.ajax=function(){return a.$.ajax.apply(a.$,arguments)};var S=a.Router=function(t){t||(t={});if(t.routes)this.routes=t.routes;this._bindRoutes();this.initialize.apply(this,arguments)};var $=/\((.*?)\)/g;var T=/(\(\?)?:\w+/g;var H=/\*\w+/g;var A=/[\-{}\[\]+?.,\\\^$|#\s]/g;h.extend(S.prototype,o,{initialize:function(){},route:function(t,e,i){if(!h.isRegExp(t))t=this._routeToRegExp(t);if(h.isFunction(e)){i=e;e=""}if(!i)i=this[e];var r=this;a.history.route(t,function(s){var n=r._extractParameters(t,s);i&&i.apply(r,n);r.trigger.apply(r,["route:"+e].concat(n));r.trigger("route",e,n);a.history.trigger("route",r,e,n)});return this},navigate:function(t,e){a.history.navigate(t,e);return this},_bindRoutes:function(){if(!this.routes)return;this.routes=h.result(this,"routes");var t,e=h.keys(this.routes);while((t=e.pop())!=null){this.route(t,this.routes[t])}},_routeToRegExp:function(t){t=t.replace(A,"\\$&").replace($,"(?:$1)?").replace(T,function(t,e){return e?t:"([^/]+)"}).replace(H,"(.*?)");return new RegExp("^"+t+"$")},_extractParameters:function(t,e){var i=t.exec(e).slice(1);return h.map(i,function(t){return t?decodeURIComponent(t):null})}});var I=a.History=function(){this.handlers=[];h.bindAll(this,"checkUrl");if(typeof window!=="undefined"){this.location=window.location;this.history=window.history}};var N=/^[#\/]|\s+$/g;var P=/^\/+|\/+$/g;var O=/msie [\w.]+/;var C=/\/$/;I.started=false;h.extend(I.prototype,o,{interval:50,getHash:function(t){var e=(t||this).location.href.match(/#(.*)$/);return e?e[1]:""},getFragment:function(t,e){if(t==null){if(this._hasPushState||!this._wantsHashChange||e){t=this.location.pathname;var i=this.root.replace(C,"");if(!t.indexOf(i))t=t.substr(i.length)}else{t=this.getHash()}}return t.replace(N,"")},start:function(t){if(I.started)throw new Error("Backbone.history has already been started");I.started=true;this.options=h.extend({},{root:"/"},this.options,t);this.root=this.options.root;this._wantsHashChange=this.options.hashChange!==false;this._wantsPushState=!!this.options.pushState;this._hasPushState=!!(this.options.pushState&&this.history&&this.history.pushState);var e=this.getFragment();var i=document.documentMode;var r=O.exec(navigator.userAgent.toLowerCase())&&(!i||i<=7);this.root=("/"+this.root+"/").replace(P,"/");if(r&&this._wantsHashChange){this.iframe=a.$('<iframe src="javascript:0" tabindex="-1" />').hide().appendTo("body")[0].contentWindow;this.navigate(e)}if(this._hasPushState){a.$(window).on("popstate",this.checkUrl)}else if(this._wantsHashChange&&"onhashchange"in window&&!r){a.$(window).on("hashchange",this.checkUrl)}else if(this._wantsHashChange){this._checkUrlInterval=setInterval(this.checkUrl,this.interval)}this.fragment=e;var s=this.location;var n=s.pathname.replace(/[^\/]$/,"$&/")===this.root;if(this._wantsHashChange&&this._wantsPushState&&!this._hasPushState&&!n){this.fragment=this.getFragment(null,true);this.location.replace(this.root+this.location.search+"#"+this.fragment);return true}else if(this._wantsPushState&&this._hasPushState&&n&&s.hash){this.fragment=this.getHash().replace(N,"");this.history.replaceState({},document.title,this.root+this.fragment+s.search)}if(!this.options.silent)return this.loadUrl()},stop:function(){a.$(window).off("popstate",this.checkUrl).off("hashchange",this.checkUrl);clearInterval(this._checkUrlInterval);I.started=false},route:function(t,e){this.handlers.unshift({route:t,callback:e})},checkUrl:function(t){var e=this.getFragment();if(e===this.fragment&&this.iframe){e=this.getFragment(this.getHash(this.iframe))}if(e===this.fragment)return false;if(this.iframe)this.navigate(e);this.loadUrl()||this.loadUrl(this.getHash())},loadUrl:function(t){var e=this.fragment=this.getFragment(t);var i=h.any(this.handlers,function(t){if(t.route.test(e)){t.callback(e);return true}});return i},navigate:function(t,e){if(!I.started)return false;if(!e||e===true)e={trigger:e};t=this.getFragment(t||"");if(this.fragment===t)return;this.fragment=t;var i=this.root+t;if(this._hasPushState){this.history[e.replace?"replaceState":"pushState"]({},document.title,i)}else if(this._wantsHashChange){this._updateHash(this.location,t,e.replace);if(this.iframe&&t!==this.getFragment(this.getHash(this.iframe))){if(!e.replace)this.iframe.document.open().close();this._updateHash(this.iframe.location,t,e.replace)}}else{return this.location.assign(i)}if(e.trigger)this.loadUrl(t)},_updateHash:function(t,e,i){if(i){var r=t.href.replace(/(javascript:|#).*$/,"");t.replace(r+"#"+e)}else{t.hash="#"+e}}});a.history=new I;var j=function(t,e){var i=this;var r;if(t&&h.has(t,"constructor")){r=t.constructor}else{r=function(){return i.apply(this,arguments)}}h.extend(r,i,e);var s=function(){this.constructor=r};s.prototype=i.prototype;r.prototype=new s;if(t)h.extend(r.prototype,t);r.__super__=i.prototype;return r};d.extend=g.extend=S.extend=b.extend=I.extend=j;var U=function(){throw new Error('A "url" property or function must be specified')};var R=function(t,e){var i=e.error;e.error=function(r){if(i)i(t,r,e);t.trigger("error",t,r,e)}}}).call(this);
+/*
+//@ sourceMappingURL=backbone-min.map
+*/
\ No newline at end of file
--- a/web/res/js/jquery.min.js Wed Apr 10 18:38:21 2013 +0200
+++ b/web/res/js/jquery.min.js Fri Jun 07 11:55:46 2013 +0200
@@ -1,5 +1,6 @@
-/*! jQuery v1.9.1 | (c) 2005, 2012 jQuery Foundation, Inc. | jquery.org/license
+/*! jQuery v2.0.0 | (c) 2005, 2013 jQuery Foundation, Inc. | jquery.org/license
//@ sourceMappingURL=jquery.min.map
-*/(function(e,t){var n,r,i=typeof t,o=e.document,a=e.location,s=e.jQuery,u=e.$,l={},c=[],p="1.9.1",f=c.concat,d=c.push,h=c.slice,g=c.indexOf,m=l.toString,y=l.hasOwnProperty,v=p.trim,b=function(e,t){return new b.fn.init(e,t,r)},x=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,w=/\S+/g,T=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,N=/^(?:(<[\w\W]+>)[^>]*|#([\w-]*))$/,C=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,k=/^[\],:{}\s]*$/,E=/(?:^|:|,)(?:\s*\[)+/g,S=/\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/g,A=/"[^"\\\r\n]*"|true|false|null|-?(?:\d+\.|)\d+(?:[eE][+-]?\d+|)/g,j=/^-ms-/,D=/-([\da-z])/gi,L=function(e,t){return t.toUpperCase()},H=function(e){(o.addEventListener||"load"===e.type||"complete"===o.readyState)&&(q(),b.ready())},q=function(){o.addEventListener?(o.removeEventListener("DOMContentLoaded",H,!1),e.removeEventListener("load",H,!1)):(o.detachEvent("onreadystatechange",H),e.detachEvent("onload",H))};b.fn=b.prototype={jquery:p,constructor:b,init:function(e,n,r){var i,a;if(!e)return this;if("string"==typeof e){if(i="<"===e.charAt(0)&&">"===e.charAt(e.length-1)&&e.length>=3?[null,e,null]:N.exec(e),!i||!i[1]&&n)return!n||n.jquery?(n||r).find(e):this.constructor(n).find(e);if(i[1]){if(n=n instanceof b?n[0]:n,b.merge(this,b.parseHTML(i[1],n&&n.nodeType?n.ownerDocument||n:o,!0)),C.test(i[1])&&b.isPlainObject(n))for(i in n)b.isFunction(this[i])?this[i](n[i]):this.attr(i,n[i]);return this}if(a=o.getElementById(i[2]),a&&a.parentNode){if(a.id!==i[2])return r.find(e);this.length=1,this[0]=a}return this.context=o,this.selector=e,this}return e.nodeType?(this.context=this[0]=e,this.length=1,this):b.isFunction(e)?r.ready(e):(e.selector!==t&&(this.selector=e.selector,this.context=e.context),b.makeArray(e,this))},selector:"",length:0,size:function(){return this.length},toArray:function(){return h.call(this)},get:function(e){return null==e?this.toArray():0>e?this[this.length+e]:this[e]},pushStack:function(e){var t=b.merge(this.constructor(),e);return t.prevObject=this,t.context=this.context,t},each:function(e,t){return b.each(this,e,t)},ready:function(e){return b.ready.promise().done(e),this},slice:function(){return this.pushStack(h.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(e){var t=this.length,n=+e+(0>e?t:0);return this.pushStack(n>=0&&t>n?[this[n]]:[])},map:function(e){return this.pushStack(b.map(this,function(t,n){return e.call(t,n,t)}))},end:function(){return this.prevObject||this.constructor(null)},push:d,sort:[].sort,splice:[].splice},b.fn.init.prototype=b.fn,b.extend=b.fn.extend=function(){var e,n,r,i,o,a,s=arguments[0]||{},u=1,l=arguments.length,c=!1;for("boolean"==typeof s&&(c=s,s=arguments[1]||{},u=2),"object"==typeof s||b.isFunction(s)||(s={}),l===u&&(s=this,--u);l>u;u++)if(null!=(o=arguments[u]))for(i in o)e=s[i],r=o[i],s!==r&&(c&&r&&(b.isPlainObject(r)||(n=b.isArray(r)))?(n?(n=!1,a=e&&b.isArray(e)?e:[]):a=e&&b.isPlainObject(e)?e:{},s[i]=b.extend(c,a,r)):r!==t&&(s[i]=r));return s},b.extend({noConflict:function(t){return e.$===b&&(e.$=u),t&&e.jQuery===b&&(e.jQuery=s),b},isReady:!1,readyWait:1,holdReady:function(e){e?b.readyWait++:b.ready(!0)},ready:function(e){if(e===!0?!--b.readyWait:!b.isReady){if(!o.body)return setTimeout(b.ready);b.isReady=!0,e!==!0&&--b.readyWait>0||(n.resolveWith(o,[b]),b.fn.trigger&&b(o).trigger("ready").off("ready"))}},isFunction:function(e){return"function"===b.type(e)},isArray:Array.isArray||function(e){return"array"===b.type(e)},isWindow:function(e){return null!=e&&e==e.window},isNumeric:function(e){return!isNaN(parseFloat(e))&&isFinite(e)},type:function(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?l[m.call(e)]||"object":typeof e},isPlainObject:function(e){if(!e||"object"!==b.type(e)||e.nodeType||b.isWindow(e))return!1;try{if(e.constructor&&!y.call(e,"constructor")&&!y.call(e.constructor.prototype,"isPrototypeOf"))return!1}catch(n){return!1}var r;for(r in e);return r===t||y.call(e,r)},isEmptyObject:function(e){var t;for(t in e)return!1;return!0},error:function(e){throw Error(e)},parseHTML:function(e,t,n){if(!e||"string"!=typeof e)return null;"boolean"==typeof t&&(n=t,t=!1),t=t||o;var r=C.exec(e),i=!n&&[];return r?[t.createElement(r[1])]:(r=b.buildFragment([e],t,i),i&&b(i).remove(),b.merge([],r.childNodes))},parseJSON:function(n){return e.JSON&&e.JSON.parse?e.JSON.parse(n):null===n?n:"string"==typeof n&&(n=b.trim(n),n&&k.test(n.replace(S,"@").replace(A,"]").replace(E,"")))?Function("return "+n)():(b.error("Invalid JSON: "+n),t)},parseXML:function(n){var r,i;if(!n||"string"!=typeof n)return null;try{e.DOMParser?(i=new DOMParser,r=i.parseFromString(n,"text/xml")):(r=new ActiveXObject("Microsoft.XMLDOM"),r.async="false",r.loadXML(n))}catch(o){r=t}return r&&r.documentElement&&!r.getElementsByTagName("parsererror").length||b.error("Invalid XML: "+n),r},noop:function(){},globalEval:function(t){t&&b.trim(t)&&(e.execScript||function(t){e.eval.call(e,t)})(t)},camelCase:function(e){return e.replace(j,"ms-").replace(D,L)},nodeName:function(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()},each:function(e,t,n){var r,i=0,o=e.length,a=M(e);if(n){if(a){for(;o>i;i++)if(r=t.apply(e[i],n),r===!1)break}else for(i in e)if(r=t.apply(e[i],n),r===!1)break}else if(a){for(;o>i;i++)if(r=t.call(e[i],i,e[i]),r===!1)break}else for(i in e)if(r=t.call(e[i],i,e[i]),r===!1)break;return e},trim:v&&!v.call("\ufeff\u00a0")?function(e){return null==e?"":v.call(e)}:function(e){return null==e?"":(e+"").replace(T,"")},makeArray:function(e,t){var n=t||[];return null!=e&&(M(Object(e))?b.merge(n,"string"==typeof e?[e]:e):d.call(n,e)),n},inArray:function(e,t,n){var r;if(t){if(g)return g.call(t,e,n);for(r=t.length,n=n?0>n?Math.max(0,r+n):n:0;r>n;n++)if(n in t&&t[n]===e)return n}return-1},merge:function(e,n){var r=n.length,i=e.length,o=0;if("number"==typeof r)for(;r>o;o++)e[i++]=n[o];else while(n[o]!==t)e[i++]=n[o++];return e.length=i,e},grep:function(e,t,n){var r,i=[],o=0,a=e.length;for(n=!!n;a>o;o++)r=!!t(e[o],o),n!==r&&i.push(e[o]);return i},map:function(e,t,n){var r,i=0,o=e.length,a=M(e),s=[];if(a)for(;o>i;i++)r=t(e[i],i,n),null!=r&&(s[s.length]=r);else for(i in e)r=t(e[i],i,n),null!=r&&(s[s.length]=r);return f.apply([],s)},guid:1,proxy:function(e,n){var r,i,o;return"string"==typeof n&&(o=e[n],n=e,e=o),b.isFunction(e)?(r=h.call(arguments,2),i=function(){return e.apply(n||this,r.concat(h.call(arguments)))},i.guid=e.guid=e.guid||b.guid++,i):t},access:function(e,n,r,i,o,a,s){var u=0,l=e.length,c=null==r;if("object"===b.type(r)){o=!0;for(u in r)b.access(e,n,u,r[u],!0,a,s)}else if(i!==t&&(o=!0,b.isFunction(i)||(s=!0),c&&(s?(n.call(e,i),n=null):(c=n,n=function(e,t,n){return c.call(b(e),n)})),n))for(;l>u;u++)n(e[u],r,s?i:i.call(e[u],u,n(e[u],r)));return o?e:c?n.call(e):l?n(e[0],r):a},now:function(){return(new Date).getTime()}}),b.ready.promise=function(t){if(!n)if(n=b.Deferred(),"complete"===o.readyState)setTimeout(b.ready);else if(o.addEventListener)o.addEventListener("DOMContentLoaded",H,!1),e.addEventListener("load",H,!1);else{o.attachEvent("onreadystatechange",H),e.attachEvent("onload",H);var r=!1;try{r=null==e.frameElement&&o.documentElement}catch(i){}r&&r.doScroll&&function a(){if(!b.isReady){try{r.doScroll("left")}catch(e){return setTimeout(a,50)}q(),b.ready()}}()}return n.promise(t)},b.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(e,t){l["[object "+t+"]"]=t.toLowerCase()});function M(e){var t=e.length,n=b.type(e);return b.isWindow(e)?!1:1===e.nodeType&&t?!0:"array"===n||"function"!==n&&(0===t||"number"==typeof t&&t>0&&t-1 in e)}r=b(o);var _={};function F(e){var t=_[e]={};return b.each(e.match(w)||[],function(e,n){t[n]=!0}),t}b.Callbacks=function(e){e="string"==typeof e?_[e]||F(e):b.extend({},e);var n,r,i,o,a,s,u=[],l=!e.once&&[],c=function(t){for(r=e.memory&&t,i=!0,a=s||0,s=0,o=u.length,n=!0;u&&o>a;a++)if(u[a].apply(t[0],t[1])===!1&&e.stopOnFalse){r=!1;break}n=!1,u&&(l?l.length&&c(l.shift()):r?u=[]:p.disable())},p={add:function(){if(u){var t=u.length;(function i(t){b.each(t,function(t,n){var r=b.type(n);"function"===r?e.unique&&p.has(n)||u.push(n):n&&n.length&&"string"!==r&&i(n)})})(arguments),n?o=u.length:r&&(s=t,c(r))}return this},remove:function(){return u&&b.each(arguments,function(e,t){var r;while((r=b.inArray(t,u,r))>-1)u.splice(r,1),n&&(o>=r&&o--,a>=r&&a--)}),this},has:function(e){return e?b.inArray(e,u)>-1:!(!u||!u.length)},empty:function(){return u=[],this},disable:function(){return u=l=r=t,this},disabled:function(){return!u},lock:function(){return l=t,r||p.disable(),this},locked:function(){return!l},fireWith:function(e,t){return t=t||[],t=[e,t.slice?t.slice():t],!u||i&&!l||(n?l.push(t):c(t)),this},fire:function(){return p.fireWith(this,arguments),this},fired:function(){return!!i}};return p},b.extend({Deferred:function(e){var t=[["resolve","done",b.Callbacks("once memory"),"resolved"],["reject","fail",b.Callbacks("once memory"),"rejected"],["notify","progress",b.Callbacks("memory")]],n="pending",r={state:function(){return n},always:function(){return i.done(arguments).fail(arguments),this},then:function(){var e=arguments;return b.Deferred(function(n){b.each(t,function(t,o){var a=o[0],s=b.isFunction(e[t])&&e[t];i[o[1]](function(){var e=s&&s.apply(this,arguments);e&&b.isFunction(e.promise)?e.promise().done(n.resolve).fail(n.reject).progress(n.notify):n[a+"With"](this===r?n.promise():this,s?[e]:arguments)})}),e=null}).promise()},promise:function(e){return null!=e?b.extend(e,r):r}},i={};return r.pipe=r.then,b.each(t,function(e,o){var a=o[2],s=o[3];r[o[1]]=a.add,s&&a.add(function(){n=s},t[1^e][2].disable,t[2][2].lock),i[o[0]]=function(){return i[o[0]+"With"](this===i?r:this,arguments),this},i[o[0]+"With"]=a.fireWith}),r.promise(i),e&&e.call(i,i),i},when:function(e){var t=0,n=h.call(arguments),r=n.length,i=1!==r||e&&b.isFunction(e.promise)?r:0,o=1===i?e:b.Deferred(),a=function(e,t,n){return function(r){t[e]=this,n[e]=arguments.length>1?h.call(arguments):r,n===s?o.notifyWith(t,n):--i||o.resolveWith(t,n)}},s,u,l;if(r>1)for(s=Array(r),u=Array(r),l=Array(r);r>t;t++)n[t]&&b.isFunction(n[t].promise)?n[t].promise().done(a(t,l,n)).fail(o.reject).progress(a(t,u,s)):--i;return i||o.resolveWith(l,n),o.promise()}}),b.support=function(){var t,n,r,a,s,u,l,c,p,f,d=o.createElement("div");if(d.setAttribute("className","t"),d.innerHTML=" <link/><table></table><a href='/a'>a</a><input type='checkbox'/>",n=d.getElementsByTagName("*"),r=d.getElementsByTagName("a")[0],!n||!r||!n.length)return{};s=o.createElement("select"),l=s.appendChild(o.createElement("option")),a=d.getElementsByTagName("input")[0],r.style.cssText="top:1px;float:left;opacity:.5",t={getSetAttribute:"t"!==d.className,leadingWhitespace:3===d.firstChild.nodeType,tbody:!d.getElementsByTagName("tbody").length,htmlSerialize:!!d.getElementsByTagName("link").length,style:/top/.test(r.getAttribute("style")),hrefNormalized:"/a"===r.getAttribute("href"),opacity:/^0.5/.test(r.style.opacity),cssFloat:!!r.style.cssFloat,checkOn:!!a.value,optSelected:l.selected,enctype:!!o.createElement("form").enctype,html5Clone:"<:nav></:nav>"!==o.createElement("nav").cloneNode(!0).outerHTML,boxModel:"CSS1Compat"===o.compatMode,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0,boxSizingReliable:!0,pixelPosition:!1},a.checked=!0,t.noCloneChecked=a.cloneNode(!0).checked,s.disabled=!0,t.optDisabled=!l.disabled;try{delete d.test}catch(h){t.deleteExpando=!1}a=o.createElement("input"),a.setAttribute("value",""),t.input=""===a.getAttribute("value"),a.value="t",a.setAttribute("type","radio"),t.radioValue="t"===a.value,a.setAttribute("checked","t"),a.setAttribute("name","t"),u=o.createDocumentFragment(),u.appendChild(a),t.appendChecked=a.checked,t.checkClone=u.cloneNode(!0).cloneNode(!0).lastChild.checked,d.attachEvent&&(d.attachEvent("onclick",function(){t.noCloneEvent=!1}),d.cloneNode(!0).click());for(f in{submit:!0,change:!0,focusin:!0})d.setAttribute(c="on"+f,"t"),t[f+"Bubbles"]=c in e||d.attributes[c].expando===!1;return d.style.backgroundClip="content-box",d.cloneNode(!0).style.backgroundClip="",t.clearCloneStyle="content-box"===d.style.backgroundClip,b(function(){var n,r,a,s="padding:0;margin:0;border:0;display:block;box-sizing:content-box;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;",u=o.getElementsByTagName("body")[0];u&&(n=o.createElement("div"),n.style.cssText="border:0;width:0;height:0;position:absolute;top:0;left:-9999px;margin-top:1px",u.appendChild(n).appendChild(d),d.innerHTML="<table><tr><td></td><td>t</td></tr></table>",a=d.getElementsByTagName("td"),a[0].style.cssText="padding:0;margin:0;border:0;display:none",p=0===a[0].offsetHeight,a[0].style.display="",a[1].style.display="none",t.reliableHiddenOffsets=p&&0===a[0].offsetHeight,d.innerHTML="",d.style.cssText="box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;padding:1px;border:1px;display:block;width:4px;margin-top:1%;position:absolute;top:1%;",t.boxSizing=4===d.offsetWidth,t.doesNotIncludeMarginInBodyOffset=1!==u.offsetTop,e.getComputedStyle&&(t.pixelPosition="1%"!==(e.getComputedStyle(d,null)||{}).top,t.boxSizingReliable="4px"===(e.getComputedStyle(d,null)||{width:"4px"}).width,r=d.appendChild(o.createElement("div")),r.style.cssText=d.style.cssText=s,r.style.marginRight=r.style.width="0",d.style.width="1px",t.reliableMarginRight=!parseFloat((e.getComputedStyle(r,null)||{}).marginRight)),typeof d.style.zoom!==i&&(d.innerHTML="",d.style.cssText=s+"width:1px;padding:1px;display:inline;zoom:1",t.inlineBlockNeedsLayout=3===d.offsetWidth,d.style.display="block",d.innerHTML="<div></div>",d.firstChild.style.width="5px",t.shrinkWrapBlocks=3!==d.offsetWidth,t.inlineBlockNeedsLayout&&(u.style.zoom=1)),u.removeChild(n),n=d=a=r=null)}),n=s=u=l=r=a=null,t}();var O=/(?:\{[\s\S]*\}|\[[\s\S]*\])$/,B=/([A-Z])/g;function P(e,n,r,i){if(b.acceptData(e)){var o,a,s=b.expando,u="string"==typeof n,l=e.nodeType,p=l?b.cache:e,f=l?e[s]:e[s]&&s;if(f&&p[f]&&(i||p[f].data)||!u||r!==t)return f||(l?e[s]=f=c.pop()||b.guid++:f=s),p[f]||(p[f]={},l||(p[f].toJSON=b.noop)),("object"==typeof n||"function"==typeof n)&&(i?p[f]=b.extend(p[f],n):p[f].data=b.extend(p[f].data,n)),o=p[f],i||(o.data||(o.data={}),o=o.data),r!==t&&(o[b.camelCase(n)]=r),u?(a=o[n],null==a&&(a=o[b.camelCase(n)])):a=o,a}}function R(e,t,n){if(b.acceptData(e)){var r,i,o,a=e.nodeType,s=a?b.cache:e,u=a?e[b.expando]:b.expando;if(s[u]){if(t&&(o=n?s[u]:s[u].data)){b.isArray(t)?t=t.concat(b.map(t,b.camelCase)):t in o?t=[t]:(t=b.camelCase(t),t=t in o?[t]:t.split(" "));for(r=0,i=t.length;i>r;r++)delete o[t[r]];if(!(n?$:b.isEmptyObject)(o))return}(n||(delete s[u].data,$(s[u])))&&(a?b.cleanData([e],!0):b.support.deleteExpando||s!=s.window?delete s[u]:s[u]=null)}}}b.extend({cache:{},expando:"jQuery"+(p+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(e){return e=e.nodeType?b.cache[e[b.expando]]:e[b.expando],!!e&&!$(e)},data:function(e,t,n){return P(e,t,n)},removeData:function(e,t){return R(e,t)},_data:function(e,t,n){return P(e,t,n,!0)},_removeData:function(e,t){return R(e,t,!0)},acceptData:function(e){if(e.nodeType&&1!==e.nodeType&&9!==e.nodeType)return!1;var t=e.nodeName&&b.noData[e.nodeName.toLowerCase()];return!t||t!==!0&&e.getAttribute("classid")===t}}),b.fn.extend({data:function(e,n){var r,i,o=this[0],a=0,s=null;if(e===t){if(this.length&&(s=b.data(o),1===o.nodeType&&!b._data(o,"parsedAttrs"))){for(r=o.attributes;r.length>a;a++)i=r[a].name,i.indexOf("data-")||(i=b.camelCase(i.slice(5)),W(o,i,s[i]));b._data(o,"parsedAttrs",!0)}return s}return"object"==typeof e?this.each(function(){b.data(this,e)}):b.access(this,function(n){return n===t?o?W(o,e,b.data(o,e)):null:(this.each(function(){b.data(this,e,n)}),t)},null,n,arguments.length>1,null,!0)},removeData:function(e){return this.each(function(){b.removeData(this,e)})}});function W(e,n,r){if(r===t&&1===e.nodeType){var i="data-"+n.replace(B,"-$1").toLowerCase();if(r=e.getAttribute(i),"string"==typeof r){try{r="true"===r?!0:"false"===r?!1:"null"===r?null:+r+""===r?+r:O.test(r)?b.parseJSON(r):r}catch(o){}b.data(e,n,r)}else r=t}return r}function $(e){var t;for(t in e)if(("data"!==t||!b.isEmptyObject(e[t]))&&"toJSON"!==t)return!1;return!0}b.extend({queue:function(e,n,r){var i;return e?(n=(n||"fx")+"queue",i=b._data(e,n),r&&(!i||b.isArray(r)?i=b._data(e,n,b.makeArray(r)):i.push(r)),i||[]):t},dequeue:function(e,t){t=t||"fx";var n=b.queue(e,t),r=n.length,i=n.shift(),o=b._queueHooks(e,t),a=function(){b.dequeue(e,t)};"inprogress"===i&&(i=n.shift(),r--),o.cur=i,i&&("fx"===t&&n.unshift("inprogress"),delete o.stop,i.call(e,a,o)),!r&&o&&o.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return b._data(e,n)||b._data(e,n,{empty:b.Callbacks("once memory").add(function(){b._removeData(e,t+"queue"),b._removeData(e,n)})})}}),b.fn.extend({queue:function(e,n){var r=2;return"string"!=typeof e&&(n=e,e="fx",r--),r>arguments.length?b.queue(this[0],e):n===t?this:this.each(function(){var t=b.queue(this,e,n);b._queueHooks(this,e),"fx"===e&&"inprogress"!==t[0]&&b.dequeue(this,e)})},dequeue:function(e){return this.each(function(){b.dequeue(this,e)})},delay:function(e,t){return e=b.fx?b.fx.speeds[e]||e:e,t=t||"fx",this.queue(t,function(t,n){var r=setTimeout(t,e);n.stop=function(){clearTimeout(r)}})},clearQueue:function(e){return this.queue(e||"fx",[])},promise:function(e,n){var r,i=1,o=b.Deferred(),a=this,s=this.length,u=function(){--i||o.resolveWith(a,[a])};"string"!=typeof e&&(n=e,e=t),e=e||"fx";while(s--)r=b._data(a[s],e+"queueHooks"),r&&r.empty&&(i++,r.empty.add(u));return u(),o.promise(n)}});var I,z,X=/[\t\r\n]/g,U=/\r/g,V=/^(?:input|select|textarea|button|object)$/i,Y=/^(?:a|area)$/i,J=/^(?:checked|selected|autofocus|autoplay|async|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped)$/i,G=/^(?:checked|selected)$/i,Q=b.support.getSetAttribute,K=b.support.input;b.fn.extend({attr:function(e,t){return b.access(this,b.attr,e,t,arguments.length>1)},removeAttr:function(e){return this.each(function(){b.removeAttr(this,e)})},prop:function(e,t){return b.access(this,b.prop,e,t,arguments.length>1)},removeProp:function(e){return e=b.propFix[e]||e,this.each(function(){try{this[e]=t,delete this[e]}catch(n){}})},addClass:function(e){var t,n,r,i,o,a=0,s=this.length,u="string"==typeof e&&e;if(b.isFunction(e))return this.each(function(t){b(this).addClass(e.call(this,t,this.className))});if(u)for(t=(e||"").match(w)||[];s>a;a++)if(n=this[a],r=1===n.nodeType&&(n.className?(" "+n.className+" ").replace(X," "):" ")){o=0;while(i=t[o++])0>r.indexOf(" "+i+" ")&&(r+=i+" ");n.className=b.trim(r)}return this},removeClass:function(e){var t,n,r,i,o,a=0,s=this.length,u=0===arguments.length||"string"==typeof e&&e;if(b.isFunction(e))return this.each(function(t){b(this).removeClass(e.call(this,t,this.className))});if(u)for(t=(e||"").match(w)||[];s>a;a++)if(n=this[a],r=1===n.nodeType&&(n.className?(" "+n.className+" ").replace(X," "):"")){o=0;while(i=t[o++])while(r.indexOf(" "+i+" ")>=0)r=r.replace(" "+i+" "," ");n.className=e?b.trim(r):""}return this},toggleClass:function(e,t){var n=typeof e,r="boolean"==typeof t;return b.isFunction(e)?this.each(function(n){b(this).toggleClass(e.call(this,n,this.className,t),t)}):this.each(function(){if("string"===n){var o,a=0,s=b(this),u=t,l=e.match(w)||[];while(o=l[a++])u=r?u:!s.hasClass(o),s[u?"addClass":"removeClass"](o)}else(n===i||"boolean"===n)&&(this.className&&b._data(this,"__className__",this.className),this.className=this.className||e===!1?"":b._data(this,"__className__")||"")})},hasClass:function(e){var t=" "+e+" ",n=0,r=this.length;for(;r>n;n++)if(1===this[n].nodeType&&(" "+this[n].className+" ").replace(X," ").indexOf(t)>=0)return!0;return!1},val:function(e){var n,r,i,o=this[0];{if(arguments.length)return i=b.isFunction(e),this.each(function(n){var o,a=b(this);1===this.nodeType&&(o=i?e.call(this,n,a.val()):e,null==o?o="":"number"==typeof o?o+="":b.isArray(o)&&(o=b.map(o,function(e){return null==e?"":e+""})),r=b.valHooks[this.type]||b.valHooks[this.nodeName.toLowerCase()],r&&"set"in r&&r.set(this,o,"value")!==t||(this.value=o))});if(o)return r=b.valHooks[o.type]||b.valHooks[o.nodeName.toLowerCase()],r&&"get"in r&&(n=r.get(o,"value"))!==t?n:(n=o.value,"string"==typeof n?n.replace(U,""):null==n?"":n)}}}),b.extend({valHooks:{option:{get:function(e){var t=e.attributes.value;return!t||t.specified?e.value:e.text}},select:{get:function(e){var t,n,r=e.options,i=e.selectedIndex,o="select-one"===e.type||0>i,a=o?null:[],s=o?i+1:r.length,u=0>i?s:o?i:0;for(;s>u;u++)if(n=r[u],!(!n.selected&&u!==i||(b.support.optDisabled?n.disabled:null!==n.getAttribute("disabled"))||n.parentNode.disabled&&b.nodeName(n.parentNode,"optgroup"))){if(t=b(n).val(),o)return t;a.push(t)}return a},set:function(e,t){var n=b.makeArray(t);return b(e).find("option").each(function(){this.selected=b.inArray(b(this).val(),n)>=0}),n.length||(e.selectedIndex=-1),n}}},attr:function(e,n,r){var o,a,s,u=e.nodeType;if(e&&3!==u&&8!==u&&2!==u)return typeof e.getAttribute===i?b.prop(e,n,r):(a=1!==u||!b.isXMLDoc(e),a&&(n=n.toLowerCase(),o=b.attrHooks[n]||(J.test(n)?z:I)),r===t?o&&a&&"get"in o&&null!==(s=o.get(e,n))?s:(typeof e.getAttribute!==i&&(s=e.getAttribute(n)),null==s?t:s):null!==r?o&&a&&"set"in o&&(s=o.set(e,r,n))!==t?s:(e.setAttribute(n,r+""),r):(b.removeAttr(e,n),t))},removeAttr:function(e,t){var n,r,i=0,o=t&&t.match(w);if(o&&1===e.nodeType)while(n=o[i++])r=b.propFix[n]||n,J.test(n)?!Q&&G.test(n)?e[b.camelCase("default-"+n)]=e[r]=!1:e[r]=!1:b.attr(e,n,""),e.removeAttribute(Q?n:r)},attrHooks:{type:{set:function(e,t){if(!b.support.radioValue&&"radio"===t&&b.nodeName(e,"input")){var n=e.value;return e.setAttribute("type",t),n&&(e.value=n),t}}}},propFix:{tabindex:"tabIndex",readonly:"readOnly","for":"htmlFor","class":"className",maxlength:"maxLength",cellspacing:"cellSpacing",cellpadding:"cellPadding",rowspan:"rowSpan",colspan:"colSpan",usemap:"useMap",frameborder:"frameBorder",contenteditable:"contentEditable"},prop:function(e,n,r){var i,o,a,s=e.nodeType;if(e&&3!==s&&8!==s&&2!==s)return a=1!==s||!b.isXMLDoc(e),a&&(n=b.propFix[n]||n,o=b.propHooks[n]),r!==t?o&&"set"in o&&(i=o.set(e,r,n))!==t?i:e[n]=r:o&&"get"in o&&null!==(i=o.get(e,n))?i:e[n]},propHooks:{tabIndex:{get:function(e){var n=e.getAttributeNode("tabindex");return n&&n.specified?parseInt(n.value,10):V.test(e.nodeName)||Y.test(e.nodeName)&&e.href?0:t}}}}),z={get:function(e,n){var r=b.prop(e,n),i="boolean"==typeof r&&e.getAttribute(n),o="boolean"==typeof r?K&&Q?null!=i:G.test(n)?e[b.camelCase("default-"+n)]:!!i:e.getAttributeNode(n);return o&&o.value!==!1?n.toLowerCase():t},set:function(e,t,n){return t===!1?b.removeAttr(e,n):K&&Q||!G.test(n)?e.setAttribute(!Q&&b.propFix[n]||n,n):e[b.camelCase("default-"+n)]=e[n]=!0,n}},K&&Q||(b.attrHooks.value={get:function(e,n){var r=e.getAttributeNode(n);return b.nodeName(e,"input")?e.defaultValue:r&&r.specified?r.value:t},set:function(e,n,r){return b.nodeName(e,"input")?(e.defaultValue=n,t):I&&I.set(e,n,r)}}),Q||(I=b.valHooks.button={get:function(e,n){var r=e.getAttributeNode(n);return r&&("id"===n||"name"===n||"coords"===n?""!==r.value:r.specified)?r.value:t},set:function(e,n,r){var i=e.getAttributeNode(r);return i||e.setAttributeNode(i=e.ownerDocument.createAttribute(r)),i.value=n+="","value"===r||n===e.getAttribute(r)?n:t}},b.attrHooks.contenteditable={get:I.get,set:function(e,t,n){I.set(e,""===t?!1:t,n)}},b.each(["width","height"],function(e,n){b.attrHooks[n]=b.extend(b.attrHooks[n],{set:function(e,r){return""===r?(e.setAttribute(n,"auto"),r):t}})})),b.support.hrefNormalized||(b.each(["href","src","width","height"],function(e,n){b.attrHooks[n]=b.extend(b.attrHooks[n],{get:function(e){var r=e.getAttribute(n,2);return null==r?t:r}})}),b.each(["href","src"],function(e,t){b.propHooks[t]={get:function(e){return e.getAttribute(t,4)}}})),b.support.style||(b.attrHooks.style={get:function(e){return e.style.cssText||t},set:function(e,t){return e.style.cssText=t+""}}),b.support.optSelected||(b.propHooks.selected=b.extend(b.propHooks.selected,{get:function(e){var t=e.parentNode;return t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex),null}})),b.support.enctype||(b.propFix.enctype="encoding"),b.support.checkOn||b.each(["radio","checkbox"],function(){b.valHooks[this]={get:function(e){return null===e.getAttribute("value")?"on":e.value}}}),b.each(["radio","checkbox"],function(){b.valHooks[this]=b.extend(b.valHooks[this],{set:function(e,n){return b.isArray(n)?e.checked=b.inArray(b(e).val(),n)>=0:t}})});var Z=/^(?:input|select|textarea)$/i,et=/^key/,tt=/^(?:mouse|contextmenu)|click/,nt=/^(?:focusinfocus|focusoutblur)$/,rt=/^([^.]*)(?:\.(.+)|)$/;function it(){return!0}function ot(){return!1}b.event={global:{},add:function(e,n,r,o,a){var s,u,l,c,p,f,d,h,g,m,y,v=b._data(e);if(v){r.handler&&(c=r,r=c.handler,a=c.selector),r.guid||(r.guid=b.guid++),(u=v.events)||(u=v.events={}),(f=v.handle)||(f=v.handle=function(e){return typeof b===i||e&&b.event.triggered===e.type?t:b.event.dispatch.apply(f.elem,arguments)},f.elem=e),n=(n||"").match(w)||[""],l=n.length;while(l--)s=rt.exec(n[l])||[],g=y=s[1],m=(s[2]||"").split(".").sort(),p=b.event.special[g]||{},g=(a?p.delegateType:p.bindType)||g,p=b.event.special[g]||{},d=b.extend({type:g,origType:y,data:o,handler:r,guid:r.guid,selector:a,needsContext:a&&b.expr.match.needsContext.test(a),namespace:m.join(".")},c),(h=u[g])||(h=u[g]=[],h.delegateCount=0,p.setup&&p.setup.call(e,o,m,f)!==!1||(e.addEventListener?e.addEventListener(g,f,!1):e.attachEvent&&e.attachEvent("on"+g,f))),p.add&&(p.add.call(e,d),d.handler.guid||(d.handler.guid=r.guid)),a?h.splice(h.delegateCount++,0,d):h.push(d),b.event.global[g]=!0;e=null}},remove:function(e,t,n,r,i){var o,a,s,u,l,c,p,f,d,h,g,m=b.hasData(e)&&b._data(e);if(m&&(c=m.events)){t=(t||"").match(w)||[""],l=t.length;while(l--)if(s=rt.exec(t[l])||[],d=g=s[1],h=(s[2]||"").split(".").sort(),d){p=b.event.special[d]||{},d=(r?p.delegateType:p.bindType)||d,f=c[d]||[],s=s[2]&&RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"),u=o=f.length;while(o--)a=f[o],!i&&g!==a.origType||n&&n.guid!==a.guid||s&&!s.test(a.namespace)||r&&r!==a.selector&&("**"!==r||!a.selector)||(f.splice(o,1),a.selector&&f.delegateCount--,p.remove&&p.remove.call(e,a));u&&!f.length&&(p.teardown&&p.teardown.call(e,h,m.handle)!==!1||b.removeEvent(e,d,m.handle),delete c[d])}else for(d in c)b.event.remove(e,d+t[l],n,r,!0);b.isEmptyObject(c)&&(delete m.handle,b._removeData(e,"events"))}},trigger:function(n,r,i,a){var s,u,l,c,p,f,d,h=[i||o],g=y.call(n,"type")?n.type:n,m=y.call(n,"namespace")?n.namespace.split("."):[];if(l=f=i=i||o,3!==i.nodeType&&8!==i.nodeType&&!nt.test(g+b.event.triggered)&&(g.indexOf(".")>=0&&(m=g.split("."),g=m.shift(),m.sort()),u=0>g.indexOf(":")&&"on"+g,n=n[b.expando]?n:new b.Event(g,"object"==typeof n&&n),n.isTrigger=!0,n.namespace=m.join("."),n.namespace_re=n.namespace?RegExp("(^|\\.)"+m.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,n.result=t,n.target||(n.target=i),r=null==r?[n]:b.makeArray(r,[n]),p=b.event.special[g]||{},a||!p.trigger||p.trigger.apply(i,r)!==!1)){if(!a&&!p.noBubble&&!b.isWindow(i)){for(c=p.delegateType||g,nt.test(c+g)||(l=l.parentNode);l;l=l.parentNode)h.push(l),f=l;f===(i.ownerDocument||o)&&h.push(f.defaultView||f.parentWindow||e)}d=0;while((l=h[d++])&&!n.isPropagationStopped())n.type=d>1?c:p.bindType||g,s=(b._data(l,"events")||{})[n.type]&&b._data(l,"handle"),s&&s.apply(l,r),s=u&&l[u],s&&b.acceptData(l)&&s.apply&&s.apply(l,r)===!1&&n.preventDefault();if(n.type=g,!(a||n.isDefaultPrevented()||p._default&&p._default.apply(i.ownerDocument,r)!==!1||"click"===g&&b.nodeName(i,"a")||!b.acceptData(i)||!u||!i[g]||b.isWindow(i))){f=i[u],f&&(i[u]=null),b.event.triggered=g;try{i[g]()}catch(v){}b.event.triggered=t,f&&(i[u]=f)}return n.result}},dispatch:function(e){e=b.event.fix(e);var n,r,i,o,a,s=[],u=h.call(arguments),l=(b._data(this,"events")||{})[e.type]||[],c=b.event.special[e.type]||{};if(u[0]=e,e.delegateTarget=this,!c.preDispatch||c.preDispatch.call(this,e)!==!1){s=b.event.handlers.call(this,e,l),n=0;while((o=s[n++])&&!e.isPropagationStopped()){e.currentTarget=o.elem,a=0;while((i=o.handlers[a++])&&!e.isImmediatePropagationStopped())(!e.namespace_re||e.namespace_re.test(i.namespace))&&(e.handleObj=i,e.data=i.data,r=((b.event.special[i.origType]||{}).handle||i.handler).apply(o.elem,u),r!==t&&(e.result=r)===!1&&(e.preventDefault(),e.stopPropagation()))}return c.postDispatch&&c.postDispatch.call(this,e),e.result}},handlers:function(e,n){var r,i,o,a,s=[],u=n.delegateCount,l=e.target;if(u&&l.nodeType&&(!e.button||"click"!==e.type))for(;l!=this;l=l.parentNode||this)if(1===l.nodeType&&(l.disabled!==!0||"click"!==e.type)){for(o=[],a=0;u>a;a++)i=n[a],r=i.selector+" ",o[r]===t&&(o[r]=i.needsContext?b(r,this).index(l)>=0:b.find(r,this,null,[l]).length),o[r]&&o.push(i);o.length&&s.push({elem:l,handlers:o})}return n.length>u&&s.push({elem:this,handlers:n.slice(u)}),s},fix:function(e){if(e[b.expando])return e;var t,n,r,i=e.type,a=e,s=this.fixHooks[i];s||(this.fixHooks[i]=s=tt.test(i)?this.mouseHooks:et.test(i)?this.keyHooks:{}),r=s.props?this.props.concat(s.props):this.props,e=new b.Event(a),t=r.length;while(t--)n=r[t],e[n]=a[n];return e.target||(e.target=a.srcElement||o),3===e.target.nodeType&&(e.target=e.target.parentNode),e.metaKey=!!e.metaKey,s.filter?s.filter(e,a):e},props:"altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),fixHooks:{},keyHooks:{props:"char charCode key keyCode".split(" "),filter:function(e,t){return null==e.which&&(e.which=null!=t.charCode?t.charCode:t.keyCode),e}},mouseHooks:{props:"button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "),filter:function(e,n){var r,i,a,s=n.button,u=n.fromElement;return null==e.pageX&&null!=n.clientX&&(i=e.target.ownerDocument||o,a=i.documentElement,r=i.body,e.pageX=n.clientX+(a&&a.scrollLeft||r&&r.scrollLeft||0)-(a&&a.clientLeft||r&&r.clientLeft||0),e.pageY=n.clientY+(a&&a.scrollTop||r&&r.scrollTop||0)-(a&&a.clientTop||r&&r.clientTop||0)),!e.relatedTarget&&u&&(e.relatedTarget=u===e.target?n.toElement:u),e.which||s===t||(e.which=1&s?1:2&s?3:4&s?2:0),e}},special:{load:{noBubble:!0},click:{trigger:function(){return b.nodeName(this,"input")&&"checkbox"===this.type&&this.click?(this.click(),!1):t}},focus:{trigger:function(){if(this!==o.activeElement&&this.focus)try{return this.focus(),!1}catch(e){}},delegateType:"focusin"},blur:{trigger:function(){return this===o.activeElement&&this.blur?(this.blur(),!1):t},delegateType:"focusout"},beforeunload:{postDispatch:function(e){e.result!==t&&(e.originalEvent.returnValue=e.result)}}},simulate:function(e,t,n,r){var i=b.extend(new b.Event,n,{type:e,isSimulated:!0,originalEvent:{}});r?b.event.trigger(i,null,t):b.event.dispatch.call(t,i),i.isDefaultPrevented()&&n.preventDefault()}},b.removeEvent=o.removeEventListener?function(e,t,n){e.removeEventListener&&e.removeEventListener(t,n,!1)}:function(e,t,n){var r="on"+t;e.detachEvent&&(typeof e[r]===i&&(e[r]=null),e.detachEvent(r,n))},b.Event=function(e,n){return this instanceof b.Event?(e&&e.type?(this.originalEvent=e,this.type=e.type,this.isDefaultPrevented=e.defaultPrevented||e.returnValue===!1||e.getPreventDefault&&e.getPreventDefault()?it:ot):this.type=e,n&&b.extend(this,n),this.timeStamp=e&&e.timeStamp||b.now(),this[b.expando]=!0,t):new b.Event(e,n)},b.Event.prototype={isDefaultPrevented:ot,isPropagationStopped:ot,isImmediatePropagationStopped:ot,preventDefault:function(){var e=this.originalEvent;this.isDefaultPrevented=it,e&&(e.preventDefault?e.preventDefault():e.returnValue=!1)},stopPropagation:function(){var e=this.originalEvent;this.isPropagationStopped=it,e&&(e.stopPropagation&&e.stopPropagation(),e.cancelBubble=!0)},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=it,this.stopPropagation()}},b.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(e,t){b.event.special[e]={delegateType:t,bindType:t,handle:function(e){var n,r=this,i=e.relatedTarget,o=e.handleObj;
-return(!i||i!==r&&!b.contains(r,i))&&(e.type=o.origType,n=o.handler.apply(this,arguments),e.type=t),n}}}),b.support.submitBubbles||(b.event.special.submit={setup:function(){return b.nodeName(this,"form")?!1:(b.event.add(this,"click._submit keypress._submit",function(e){var n=e.target,r=b.nodeName(n,"input")||b.nodeName(n,"button")?n.form:t;r&&!b._data(r,"submitBubbles")&&(b.event.add(r,"submit._submit",function(e){e._submit_bubble=!0}),b._data(r,"submitBubbles",!0))}),t)},postDispatch:function(e){e._submit_bubble&&(delete e._submit_bubble,this.parentNode&&!e.isTrigger&&b.event.simulate("submit",this.parentNode,e,!0))},teardown:function(){return b.nodeName(this,"form")?!1:(b.event.remove(this,"._submit"),t)}}),b.support.changeBubbles||(b.event.special.change={setup:function(){return Z.test(this.nodeName)?(("checkbox"===this.type||"radio"===this.type)&&(b.event.add(this,"propertychange._change",function(e){"checked"===e.originalEvent.propertyName&&(this._just_changed=!0)}),b.event.add(this,"click._change",function(e){this._just_changed&&!e.isTrigger&&(this._just_changed=!1),b.event.simulate("change",this,e,!0)})),!1):(b.event.add(this,"beforeactivate._change",function(e){var t=e.target;Z.test(t.nodeName)&&!b._data(t,"changeBubbles")&&(b.event.add(t,"change._change",function(e){!this.parentNode||e.isSimulated||e.isTrigger||b.event.simulate("change",this.parentNode,e,!0)}),b._data(t,"changeBubbles",!0))}),t)},handle:function(e){var n=e.target;return this!==n||e.isSimulated||e.isTrigger||"radio"!==n.type&&"checkbox"!==n.type?e.handleObj.handler.apply(this,arguments):t},teardown:function(){return b.event.remove(this,"._change"),!Z.test(this.nodeName)}}),b.support.focusinBubbles||b.each({focus:"focusin",blur:"focusout"},function(e,t){var n=0,r=function(e){b.event.simulate(t,e.target,b.event.fix(e),!0)};b.event.special[t]={setup:function(){0===n++&&o.addEventListener(e,r,!0)},teardown:function(){0===--n&&o.removeEventListener(e,r,!0)}}}),b.fn.extend({on:function(e,n,r,i,o){var a,s;if("object"==typeof e){"string"!=typeof n&&(r=r||n,n=t);for(a in e)this.on(a,n,r,e[a],o);return this}if(null==r&&null==i?(i=n,r=n=t):null==i&&("string"==typeof n?(i=r,r=t):(i=r,r=n,n=t)),i===!1)i=ot;else if(!i)return this;return 1===o&&(s=i,i=function(e){return b().off(e),s.apply(this,arguments)},i.guid=s.guid||(s.guid=b.guid++)),this.each(function(){b.event.add(this,e,i,r,n)})},one:function(e,t,n,r){return this.on(e,t,n,r,1)},off:function(e,n,r){var i,o;if(e&&e.preventDefault&&e.handleObj)return i=e.handleObj,b(e.delegateTarget).off(i.namespace?i.origType+"."+i.namespace:i.origType,i.selector,i.handler),this;if("object"==typeof e){for(o in e)this.off(o,n,e[o]);return this}return(n===!1||"function"==typeof n)&&(r=n,n=t),r===!1&&(r=ot),this.each(function(){b.event.remove(this,e,r,n)})},bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)},trigger:function(e,t){return this.each(function(){b.event.trigger(e,t,this)})},triggerHandler:function(e,n){var r=this[0];return r?b.event.trigger(e,n,r,!0):t}}),function(e,t){var n,r,i,o,a,s,u,l,c,p,f,d,h,g,m,y,v,x="sizzle"+-new Date,w=e.document,T={},N=0,C=0,k=it(),E=it(),S=it(),A=typeof t,j=1<<31,D=[],L=D.pop,H=D.push,q=D.slice,M=D.indexOf||function(e){var t=0,n=this.length;for(;n>t;t++)if(this[t]===e)return t;return-1},_="[\\x20\\t\\r\\n\\f]",F="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",O=F.replace("w","w#"),B="([*^$|!~]?=)",P="\\["+_+"*("+F+")"+_+"*(?:"+B+_+"*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|("+O+")|)|)"+_+"*\\]",R=":("+F+")(?:\\(((['\"])((?:\\\\.|[^\\\\])*?)\\3|((?:\\\\.|[^\\\\()[\\]]|"+P.replace(3,8)+")*)|.*)\\)|)",W=RegExp("^"+_+"+|((?:^|[^\\\\])(?:\\\\.)*)"+_+"+$","g"),$=RegExp("^"+_+"*,"+_+"*"),I=RegExp("^"+_+"*([\\x20\\t\\r\\n\\f>+~])"+_+"*"),z=RegExp(R),X=RegExp("^"+O+"$"),U={ID:RegExp("^#("+F+")"),CLASS:RegExp("^\\.("+F+")"),NAME:RegExp("^\\[name=['\"]?("+F+")['\"]?\\]"),TAG:RegExp("^("+F.replace("w","w*")+")"),ATTR:RegExp("^"+P),PSEUDO:RegExp("^"+R),CHILD:RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+_+"*(even|odd|(([+-]|)(\\d*)n|)"+_+"*(?:([+-]|)"+_+"*(\\d+)|))"+_+"*\\)|)","i"),needsContext:RegExp("^"+_+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+_+"*((?:-\\d)?\\d*)"+_+"*\\)|)(?=[^-]|$)","i")},V=/[\x20\t\r\n\f]*[+~]/,Y=/^[^{]+\{\s*\[native code/,J=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,G=/^(?:input|select|textarea|button)$/i,Q=/^h\d$/i,K=/'|\\/g,Z=/\=[\x20\t\r\n\f]*([^'"\]]*)[\x20\t\r\n\f]*\]/g,et=/\\([\da-fA-F]{1,6}[\x20\t\r\n\f]?|.)/g,tt=function(e,t){var n="0x"+t-65536;return n!==n?t:0>n?String.fromCharCode(n+65536):String.fromCharCode(55296|n>>10,56320|1023&n)};try{q.call(w.documentElement.childNodes,0)[0].nodeType}catch(nt){q=function(e){var t,n=[];while(t=this[e++])n.push(t);return n}}function rt(e){return Y.test(e+"")}function it(){var e,t=[];return e=function(n,r){return t.push(n+=" ")>i.cacheLength&&delete e[t.shift()],e[n]=r}}function ot(e){return e[x]=!0,e}function at(e){var t=p.createElement("div");try{return e(t)}catch(n){return!1}finally{t=null}}function st(e,t,n,r){var i,o,a,s,u,l,f,g,m,v;if((t?t.ownerDocument||t:w)!==p&&c(t),t=t||p,n=n||[],!e||"string"!=typeof e)return n;if(1!==(s=t.nodeType)&&9!==s)return[];if(!d&&!r){if(i=J.exec(e))if(a=i[1]){if(9===s){if(o=t.getElementById(a),!o||!o.parentNode)return n;if(o.id===a)return n.push(o),n}else if(t.ownerDocument&&(o=t.ownerDocument.getElementById(a))&&y(t,o)&&o.id===a)return n.push(o),n}else{if(i[2])return H.apply(n,q.call(t.getElementsByTagName(e),0)),n;if((a=i[3])&&T.getByClassName&&t.getElementsByClassName)return H.apply(n,q.call(t.getElementsByClassName(a),0)),n}if(T.qsa&&!h.test(e)){if(f=!0,g=x,m=t,v=9===s&&e,1===s&&"object"!==t.nodeName.toLowerCase()){l=ft(e),(f=t.getAttribute("id"))?g=f.replace(K,"\\$&"):t.setAttribute("id",g),g="[id='"+g+"'] ",u=l.length;while(u--)l[u]=g+dt(l[u]);m=V.test(e)&&t.parentNode||t,v=l.join(",")}if(v)try{return H.apply(n,q.call(m.querySelectorAll(v),0)),n}catch(b){}finally{f||t.removeAttribute("id")}}}return wt(e.replace(W,"$1"),t,n,r)}a=st.isXML=function(e){var t=e&&(e.ownerDocument||e).documentElement;return t?"HTML"!==t.nodeName:!1},c=st.setDocument=function(e){var n=e?e.ownerDocument||e:w;return n!==p&&9===n.nodeType&&n.documentElement?(p=n,f=n.documentElement,d=a(n),T.tagNameNoComments=at(function(e){return e.appendChild(n.createComment("")),!e.getElementsByTagName("*").length}),T.attributes=at(function(e){e.innerHTML="<select></select>";var t=typeof e.lastChild.getAttribute("multiple");return"boolean"!==t&&"string"!==t}),T.getByClassName=at(function(e){return e.innerHTML="<div class='hidden e'></div><div class='hidden'></div>",e.getElementsByClassName&&e.getElementsByClassName("e").length?(e.lastChild.className="e",2===e.getElementsByClassName("e").length):!1}),T.getByName=at(function(e){e.id=x+0,e.innerHTML="<a name='"+x+"'></a><div name='"+x+"'></div>",f.insertBefore(e,f.firstChild);var t=n.getElementsByName&&n.getElementsByName(x).length===2+n.getElementsByName(x+0).length;return T.getIdNotName=!n.getElementById(x),f.removeChild(e),t}),i.attrHandle=at(function(e){return e.innerHTML="<a href='#'></a>",e.firstChild&&typeof e.firstChild.getAttribute!==A&&"#"===e.firstChild.getAttribute("href")})?{}:{href:function(e){return e.getAttribute("href",2)},type:function(e){return e.getAttribute("type")}},T.getIdNotName?(i.find.ID=function(e,t){if(typeof t.getElementById!==A&&!d){var n=t.getElementById(e);return n&&n.parentNode?[n]:[]}},i.filter.ID=function(e){var t=e.replace(et,tt);return function(e){return e.getAttribute("id")===t}}):(i.find.ID=function(e,n){if(typeof n.getElementById!==A&&!d){var r=n.getElementById(e);return r?r.id===e||typeof r.getAttributeNode!==A&&r.getAttributeNode("id").value===e?[r]:t:[]}},i.filter.ID=function(e){var t=e.replace(et,tt);return function(e){var n=typeof e.getAttributeNode!==A&&e.getAttributeNode("id");return n&&n.value===t}}),i.find.TAG=T.tagNameNoComments?function(e,n){return typeof n.getElementsByTagName!==A?n.getElementsByTagName(e):t}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},i.find.NAME=T.getByName&&function(e,n){return typeof n.getElementsByName!==A?n.getElementsByName(name):t},i.find.CLASS=T.getByClassName&&function(e,n){return typeof n.getElementsByClassName===A||d?t:n.getElementsByClassName(e)},g=[],h=[":focus"],(T.qsa=rt(n.querySelectorAll))&&(at(function(e){e.innerHTML="<select><option selected=''></option></select>",e.querySelectorAll("[selected]").length||h.push("\\["+_+"*(?:checked|disabled|ismap|multiple|readonly|selected|value)"),e.querySelectorAll(":checked").length||h.push(":checked")}),at(function(e){e.innerHTML="<input type='hidden' i=''/>",e.querySelectorAll("[i^='']").length&&h.push("[*^$]="+_+"*(?:\"\"|'')"),e.querySelectorAll(":enabled").length||h.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),h.push(",.*:")})),(T.matchesSelector=rt(m=f.matchesSelector||f.mozMatchesSelector||f.webkitMatchesSelector||f.oMatchesSelector||f.msMatchesSelector))&&at(function(e){T.disconnectedMatch=m.call(e,"div"),m.call(e,"[s!='']:x"),g.push("!=",R)}),h=RegExp(h.join("|")),g=RegExp(g.join("|")),y=rt(f.contains)||f.compareDocumentPosition?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},v=f.compareDocumentPosition?function(e,t){var r;return e===t?(u=!0,0):(r=t.compareDocumentPosition&&e.compareDocumentPosition&&e.compareDocumentPosition(t))?1&r||e.parentNode&&11===e.parentNode.nodeType?e===n||y(w,e)?-1:t===n||y(w,t)?1:0:4&r?-1:1:e.compareDocumentPosition?-1:1}:function(e,t){var r,i=0,o=e.parentNode,a=t.parentNode,s=[e],l=[t];if(e===t)return u=!0,0;if(!o||!a)return e===n?-1:t===n?1:o?-1:a?1:0;if(o===a)return ut(e,t);r=e;while(r=r.parentNode)s.unshift(r);r=t;while(r=r.parentNode)l.unshift(r);while(s[i]===l[i])i++;return i?ut(s[i],l[i]):s[i]===w?-1:l[i]===w?1:0},u=!1,[0,0].sort(v),T.detectDuplicates=u,p):p},st.matches=function(e,t){return st(e,null,null,t)},st.matchesSelector=function(e,t){if((e.ownerDocument||e)!==p&&c(e),t=t.replace(Z,"='$1']"),!(!T.matchesSelector||d||g&&g.test(t)||h.test(t)))try{var n=m.call(e,t);if(n||T.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(r){}return st(t,p,null,[e]).length>0},st.contains=function(e,t){return(e.ownerDocument||e)!==p&&c(e),y(e,t)},st.attr=function(e,t){var n;return(e.ownerDocument||e)!==p&&c(e),d||(t=t.toLowerCase()),(n=i.attrHandle[t])?n(e):d||T.attributes?e.getAttribute(t):((n=e.getAttributeNode(t))||e.getAttribute(t))&&e[t]===!0?t:n&&n.specified?n.value:null},st.error=function(e){throw Error("Syntax error, unrecognized expression: "+e)},st.uniqueSort=function(e){var t,n=[],r=1,i=0;if(u=!T.detectDuplicates,e.sort(v),u){for(;t=e[r];r++)t===e[r-1]&&(i=n.push(r));while(i--)e.splice(n[i],1)}return e};function ut(e,t){var n=t&&e,r=n&&(~t.sourceIndex||j)-(~e.sourceIndex||j);if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function lt(e){return function(t){var n=t.nodeName.toLowerCase();return"input"===n&&t.type===e}}function ct(e){return function(t){var n=t.nodeName.toLowerCase();return("input"===n||"button"===n)&&t.type===e}}function pt(e){return ot(function(t){return t=+t,ot(function(n,r){var i,o=e([],n.length,t),a=o.length;while(a--)n[i=o[a]]&&(n[i]=!(r[i]=n[i]))})})}o=st.getText=function(e){var t,n="",r=0,i=e.nodeType;if(i){if(1===i||9===i||11===i){if("string"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=o(e)}else if(3===i||4===i)return e.nodeValue}else for(;t=e[r];r++)n+=o(t);return n},i=st.selectors={cacheLength:50,createPseudo:ot,match:U,find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(et,tt),e[3]=(e[4]||e[5]||"").replace(et,tt),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||st.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&st.error(e[0]),e},PSEUDO:function(e){var t,n=!e[5]&&e[2];return U.CHILD.test(e[0])?null:(e[4]?e[2]=e[4]:n&&z.test(n)&&(t=ft(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){return"*"===e?function(){return!0}:(e=e.replace(et,tt).toLowerCase(),function(t){return t.nodeName&&t.nodeName.toLowerCase()===e})},CLASS:function(e){var t=k[e+" "];return t||(t=RegExp("(^|"+_+")"+e+"("+_+"|$)"))&&k(e,function(e){return t.test(e.className||typeof e.getAttribute!==A&&e.getAttribute("class")||"")})},ATTR:function(e,t,n){return function(r){var i=st.attr(r,e);return null==i?"!="===t:t?(i+="","="===t?i===n:"!="===t?i!==n:"^="===t?n&&0===i.indexOf(n):"*="===t?n&&i.indexOf(n)>-1:"$="===t?n&&i.slice(-n.length)===n:"~="===t?(" "+i+" ").indexOf(n)>-1:"|="===t?i===n||i.slice(0,n.length+1)===n+"-":!1):!0}},CHILD:function(e,t,n,r,i){var o="nth"!==e.slice(0,3),a="last"!==e.slice(-4),s="of-type"===t;return 1===r&&0===i?function(e){return!!e.parentNode}:function(t,n,u){var l,c,p,f,d,h,g=o!==a?"nextSibling":"previousSibling",m=t.parentNode,y=s&&t.nodeName.toLowerCase(),v=!u&&!s;if(m){if(o){while(g){p=t;while(p=p[g])if(s?p.nodeName.toLowerCase()===y:1===p.nodeType)return!1;h=g="only"===e&&!h&&"nextSibling"}return!0}if(h=[a?m.firstChild:m.lastChild],a&&v){c=m[x]||(m[x]={}),l=c[e]||[],d=l[0]===N&&l[1],f=l[0]===N&&l[2],p=d&&m.childNodes[d];while(p=++d&&p&&p[g]||(f=d=0)||h.pop())if(1===p.nodeType&&++f&&p===t){c[e]=[N,d,f];break}}else if(v&&(l=(t[x]||(t[x]={}))[e])&&l[0]===N)f=l[1];else while(p=++d&&p&&p[g]||(f=d=0)||h.pop())if((s?p.nodeName.toLowerCase()===y:1===p.nodeType)&&++f&&(v&&((p[x]||(p[x]={}))[e]=[N,f]),p===t))break;return f-=i,f===r||0===f%r&&f/r>=0}}},PSEUDO:function(e,t){var n,r=i.pseudos[e]||i.setFilters[e.toLowerCase()]||st.error("unsupported pseudo: "+e);return r[x]?r(t):r.length>1?(n=[e,e,"",t],i.setFilters.hasOwnProperty(e.toLowerCase())?ot(function(e,n){var i,o=r(e,t),a=o.length;while(a--)i=M.call(e,o[a]),e[i]=!(n[i]=o[a])}):function(e){return r(e,0,n)}):r}},pseudos:{not:ot(function(e){var t=[],n=[],r=s(e.replace(W,"$1"));return r[x]?ot(function(e,t,n,i){var o,a=r(e,null,i,[]),s=e.length;while(s--)(o=a[s])&&(e[s]=!(t[s]=o))}):function(e,i,o){return t[0]=e,r(t,null,o,n),!n.pop()}}),has:ot(function(e){return function(t){return st(e,t).length>0}}),contains:ot(function(e){return function(t){return(t.textContent||t.innerText||o(t)).indexOf(e)>-1}}),lang:ot(function(e){return X.test(e||"")||st.error("unsupported lang: "+e),e=e.replace(et,tt).toLowerCase(),function(t){var n;do if(n=d?t.getAttribute("xml:lang")||t.getAttribute("lang"):t.lang)return n=n.toLowerCase(),n===e||0===n.indexOf(e+"-");while((t=t.parentNode)&&1===t.nodeType);return!1}}),target:function(t){var n=e.location&&e.location.hash;return n&&n.slice(1)===t.id},root:function(e){return e===f},focus:function(e){return e===p.activeElement&&(!p.hasFocus||p.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:function(e){return e.disabled===!1},disabled:function(e){return e.disabled===!0},checked:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&!!e.checked||"option"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,e.selected===!0},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeName>"@"||3===e.nodeType||4===e.nodeType)return!1;return!0},parent:function(e){return!i.pseudos.empty(e)},header:function(e){return Q.test(e.nodeName)},input:function(e){return G.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&"button"===e.type||"button"===t},text:function(e){var t;return"input"===e.nodeName.toLowerCase()&&"text"===e.type&&(null==(t=e.getAttribute("type"))||t.toLowerCase()===e.type)},first:pt(function(){return[0]}),last:pt(function(e,t){return[t-1]}),eq:pt(function(e,t,n){return[0>n?n+t:n]}),even:pt(function(e,t){var n=0;for(;t>n;n+=2)e.push(n);return e}),odd:pt(function(e,t){var n=1;for(;t>n;n+=2)e.push(n);return e}),lt:pt(function(e,t,n){var r=0>n?n+t:n;for(;--r>=0;)e.push(r);return e}),gt:pt(function(e,t,n){var r=0>n?n+t:n;for(;t>++r;)e.push(r);return e})}};for(n in{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})i.pseudos[n]=lt(n);for(n in{submit:!0,reset:!0})i.pseudos[n]=ct(n);function ft(e,t){var n,r,o,a,s,u,l,c=E[e+" "];if(c)return t?0:c.slice(0);s=e,u=[],l=i.preFilter;while(s){(!n||(r=$.exec(s)))&&(r&&(s=s.slice(r[0].length)||s),u.push(o=[])),n=!1,(r=I.exec(s))&&(n=r.shift(),o.push({value:n,type:r[0].replace(W," ")}),s=s.slice(n.length));for(a in i.filter)!(r=U[a].exec(s))||l[a]&&!(r=l[a](r))||(n=r.shift(),o.push({value:n,type:a,matches:r}),s=s.slice(n.length));if(!n)break}return t?s.length:s?st.error(e):E(e,u).slice(0)}function dt(e){var t=0,n=e.length,r="";for(;n>t;t++)r+=e[t].value;return r}function ht(e,t,n){var i=t.dir,o=n&&"parentNode"===i,a=C++;return t.first?function(t,n,r){while(t=t[i])if(1===t.nodeType||o)return e(t,n,r)}:function(t,n,s){var u,l,c,p=N+" "+a;if(s){while(t=t[i])if((1===t.nodeType||o)&&e(t,n,s))return!0}else while(t=t[i])if(1===t.nodeType||o)if(c=t[x]||(t[x]={}),(l=c[i])&&l[0]===p){if((u=l[1])===!0||u===r)return u===!0}else if(l=c[i]=[p],l[1]=e(t,n,s)||r,l[1]===!0)return!0}}function gt(e){return e.length>1?function(t,n,r){var i=e.length;while(i--)if(!e[i](t,n,r))return!1;return!0}:e[0]}function mt(e,t,n,r,i){var o,a=[],s=0,u=e.length,l=null!=t;for(;u>s;s++)(o=e[s])&&(!n||n(o,r,i))&&(a.push(o),l&&t.push(s));return a}function yt(e,t,n,r,i,o){return r&&!r[x]&&(r=yt(r)),i&&!i[x]&&(i=yt(i,o)),ot(function(o,a,s,u){var l,c,p,f=[],d=[],h=a.length,g=o||xt(t||"*",s.nodeType?[s]:s,[]),m=!e||!o&&t?g:mt(g,f,e,s,u),y=n?i||(o?e:h||r)?[]:a:m;if(n&&n(m,y,s,u),r){l=mt(y,d),r(l,[],s,u),c=l.length;while(c--)(p=l[c])&&(y[d[c]]=!(m[d[c]]=p))}if(o){if(i||e){if(i){l=[],c=y.length;while(c--)(p=y[c])&&l.push(m[c]=p);i(null,y=[],l,u)}c=y.length;while(c--)(p=y[c])&&(l=i?M.call(o,p):f[c])>-1&&(o[l]=!(a[l]=p))}}else y=mt(y===a?y.splice(h,y.length):y),i?i(null,a,y,u):H.apply(a,y)})}function vt(e){var t,n,r,o=e.length,a=i.relative[e[0].type],s=a||i.relative[" "],u=a?1:0,c=ht(function(e){return e===t},s,!0),p=ht(function(e){return M.call(t,e)>-1},s,!0),f=[function(e,n,r){return!a&&(r||n!==l)||((t=n).nodeType?c(e,n,r):p(e,n,r))}];for(;o>u;u++)if(n=i.relative[e[u].type])f=[ht(gt(f),n)];else{if(n=i.filter[e[u].type].apply(null,e[u].matches),n[x]){for(r=++u;o>r;r++)if(i.relative[e[r].type])break;return yt(u>1&>(f),u>1&&dt(e.slice(0,u-1)).replace(W,"$1"),n,r>u&&vt(e.slice(u,r)),o>r&&vt(e=e.slice(r)),o>r&&dt(e))}f.push(n)}return gt(f)}function bt(e,t){var n=0,o=t.length>0,a=e.length>0,s=function(s,u,c,f,d){var h,g,m,y=[],v=0,b="0",x=s&&[],w=null!=d,T=l,C=s||a&&i.find.TAG("*",d&&u.parentNode||u),k=N+=null==T?1:Math.random()||.1;for(w&&(l=u!==p&&u,r=n);null!=(h=C[b]);b++){if(a&&h){g=0;while(m=e[g++])if(m(h,u,c)){f.push(h);break}w&&(N=k,r=++n)}o&&((h=!m&&h)&&v--,s&&x.push(h))}if(v+=b,o&&b!==v){g=0;while(m=t[g++])m(x,y,u,c);if(s){if(v>0)while(b--)x[b]||y[b]||(y[b]=L.call(f));y=mt(y)}H.apply(f,y),w&&!s&&y.length>0&&v+t.length>1&&st.uniqueSort(f)}return w&&(N=k,l=T),x};return o?ot(s):s}s=st.compile=function(e,t){var n,r=[],i=[],o=S[e+" "];if(!o){t||(t=ft(e)),n=t.length;while(n--)o=vt(t[n]),o[x]?r.push(o):i.push(o);o=S(e,bt(i,r))}return o};function xt(e,t,n){var r=0,i=t.length;for(;i>r;r++)st(e,t[r],n);return n}function wt(e,t,n,r){var o,a,u,l,c,p=ft(e);if(!r&&1===p.length){if(a=p[0]=p[0].slice(0),a.length>2&&"ID"===(u=a[0]).type&&9===t.nodeType&&!d&&i.relative[a[1].type]){if(t=i.find.ID(u.matches[0].replace(et,tt),t)[0],!t)return n;e=e.slice(a.shift().value.length)}o=U.needsContext.test(e)?0:a.length;while(o--){if(u=a[o],i.relative[l=u.type])break;if((c=i.find[l])&&(r=c(u.matches[0].replace(et,tt),V.test(a[0].type)&&t.parentNode||t))){if(a.splice(o,1),e=r.length&&dt(a),!e)return H.apply(n,q.call(r,0)),n;break}}}return s(e,p)(r,t,d,n,V.test(e)),n}i.pseudos.nth=i.pseudos.eq;function Tt(){}i.filters=Tt.prototype=i.pseudos,i.setFilters=new Tt,c(),st.attr=b.attr,b.find=st,b.expr=st.selectors,b.expr[":"]=b.expr.pseudos,b.unique=st.uniqueSort,b.text=st.getText,b.isXMLDoc=st.isXML,b.contains=st.contains}(e);var at=/Until$/,st=/^(?:parents|prev(?:Until|All))/,ut=/^.[^:#\[\.,]*$/,lt=b.expr.match.needsContext,ct={children:!0,contents:!0,next:!0,prev:!0};b.fn.extend({find:function(e){var t,n,r,i=this.length;if("string"!=typeof e)return r=this,this.pushStack(b(e).filter(function(){for(t=0;i>t;t++)if(b.contains(r[t],this))return!0}));for(n=[],t=0;i>t;t++)b.find(e,this[t],n);return n=this.pushStack(i>1?b.unique(n):n),n.selector=(this.selector?this.selector+" ":"")+e,n},has:function(e){var t,n=b(e,this),r=n.length;return this.filter(function(){for(t=0;r>t;t++)if(b.contains(this,n[t]))return!0})},not:function(e){return this.pushStack(ft(this,e,!1))},filter:function(e){return this.pushStack(ft(this,e,!0))},is:function(e){return!!e&&("string"==typeof e?lt.test(e)?b(e,this.context).index(this[0])>=0:b.filter(e,this).length>0:this.filter(e).length>0)},closest:function(e,t){var n,r=0,i=this.length,o=[],a=lt.test(e)||"string"!=typeof e?b(e,t||this.context):0;for(;i>r;r++){n=this[r];while(n&&n.ownerDocument&&n!==t&&11!==n.nodeType){if(a?a.index(n)>-1:b.find.matchesSelector(n,e)){o.push(n);break}n=n.parentNode}}return this.pushStack(o.length>1?b.unique(o):o)},index:function(e){return e?"string"==typeof e?b.inArray(this[0],b(e)):b.inArray(e.jquery?e[0]:e,this):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){var n="string"==typeof e?b(e,t):b.makeArray(e&&e.nodeType?[e]:e),r=b.merge(this.get(),n);return this.pushStack(b.unique(r))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}}),b.fn.andSelf=b.fn.addBack;function pt(e,t){do e=e[t];while(e&&1!==e.nodeType);return e}b.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return b.dir(e,"parentNode")},parentsUntil:function(e,t,n){return b.dir(e,"parentNode",n)},next:function(e){return pt(e,"nextSibling")},prev:function(e){return pt(e,"previousSibling")},nextAll:function(e){return b.dir(e,"nextSibling")},prevAll:function(e){return b.dir(e,"previousSibling")},nextUntil:function(e,t,n){return b.dir(e,"nextSibling",n)},prevUntil:function(e,t,n){return b.dir(e,"previousSibling",n)},siblings:function(e){return b.sibling((e.parentNode||{}).firstChild,e)},children:function(e){return b.sibling(e.firstChild)},contents:function(e){return b.nodeName(e,"iframe")?e.contentDocument||e.contentWindow.document:b.merge([],e.childNodes)}},function(e,t){b.fn[e]=function(n,r){var i=b.map(this,t,n);return at.test(e)||(r=n),r&&"string"==typeof r&&(i=b.filter(r,i)),i=this.length>1&&!ct[e]?b.unique(i):i,this.length>1&&st.test(e)&&(i=i.reverse()),this.pushStack(i)}}),b.extend({filter:function(e,t,n){return n&&(e=":not("+e+")"),1===t.length?b.find.matchesSelector(t[0],e)?[t[0]]:[]:b.find.matches(e,t)},dir:function(e,n,r){var i=[],o=e[n];while(o&&9!==o.nodeType&&(r===t||1!==o.nodeType||!b(o).is(r)))1===o.nodeType&&i.push(o),o=o[n];return i},sibling:function(e,t){var n=[];for(;e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n}});function ft(e,t,n){if(t=t||0,b.isFunction(t))return b.grep(e,function(e,r){var i=!!t.call(e,r,e);return i===n});if(t.nodeType)return b.grep(e,function(e){return e===t===n});if("string"==typeof t){var r=b.grep(e,function(e){return 1===e.nodeType});if(ut.test(t))return b.filter(t,r,!n);t=b.filter(t,r)}return b.grep(e,function(e){return b.inArray(e,t)>=0===n})}function dt(e){var t=ht.split("|"),n=e.createDocumentFragment();if(n.createElement)while(t.length)n.createElement(t.pop());return n}var ht="abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",gt=/ jQuery\d+="(?:null|\d+)"/g,mt=RegExp("<(?:"+ht+")[\\s/>]","i"),yt=/^\s+/,vt=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,bt=/<([\w:]+)/,xt=/<tbody/i,wt=/<|&#?\w+;/,Tt=/<(?:script|style|link)/i,Nt=/^(?:checkbox|radio)$/i,Ct=/checked\s*(?:[^=]|=\s*.checked.)/i,kt=/^$|\/(?:java|ecma)script/i,Et=/^true\/(.*)/,St=/^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\s*$/g,At={option:[1,"<select multiple='multiple'>","</select>"],legend:[1,"<fieldset>","</fieldset>"],area:[1,"<map>","</map>"],param:[1,"<object>","</object>"],thead:[1,"<table>","</table>"],tr:[2,"<table><tbody>","</tbody></table>"],col:[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],_default:b.support.htmlSerialize?[0,"",""]:[1,"X<div>","</div>"]},jt=dt(o),Dt=jt.appendChild(o.createElement("div"));At.optgroup=At.option,At.tbody=At.tfoot=At.colgroup=At.caption=At.thead,At.th=At.td,b.fn.extend({text:function(e){return b.access(this,function(e){return e===t?b.text(this):this.empty().append((this[0]&&this[0].ownerDocument||o).createTextNode(e))},null,e,arguments.length)},wrapAll:function(e){if(b.isFunction(e))return this.each(function(t){b(this).wrapAll(e.call(this,t))});if(this[0]){var t=b(e,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){var e=this;while(e.firstChild&&1===e.firstChild.nodeType)e=e.firstChild;return e}).append(this)}return this},wrapInner:function(e){return b.isFunction(e)?this.each(function(t){b(this).wrapInner(e.call(this,t))}):this.each(function(){var t=b(this),n=t.contents();n.length?n.wrapAll(e):t.append(e)})},wrap:function(e){var t=b.isFunction(e);return this.each(function(n){b(this).wrapAll(t?e.call(this,n):e)})},unwrap:function(){return this.parent().each(function(){b.nodeName(this,"body")||b(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(e){(1===this.nodeType||11===this.nodeType||9===this.nodeType)&&this.appendChild(e)})},prepend:function(){return this.domManip(arguments,!0,function(e){(1===this.nodeType||11===this.nodeType||9===this.nodeType)&&this.insertBefore(e,this.firstChild)})},before:function(){return this.domManip(arguments,!1,function(e){this.parentNode&&this.parentNode.insertBefore(e,this)})},after:function(){return this.domManip(arguments,!1,function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)})},remove:function(e,t){var n,r=0;for(;null!=(n=this[r]);r++)(!e||b.filter(e,[n]).length>0)&&(t||1!==n.nodeType||b.cleanData(Ot(n)),n.parentNode&&(t&&b.contains(n.ownerDocument,n)&&Mt(Ot(n,"script")),n.parentNode.removeChild(n)));return this},empty:function(){var e,t=0;for(;null!=(e=this[t]);t++){1===e.nodeType&&b.cleanData(Ot(e,!1));while(e.firstChild)e.removeChild(e.firstChild);e.options&&b.nodeName(e,"select")&&(e.options.length=0)}return this},clone:function(e,t){return e=null==e?!1:e,t=null==t?e:t,this.map(function(){return b.clone(this,e,t)})},html:function(e){return b.access(this,function(e){var n=this[0]||{},r=0,i=this.length;if(e===t)return 1===n.nodeType?n.innerHTML.replace(gt,""):t;if(!("string"!=typeof e||Tt.test(e)||!b.support.htmlSerialize&&mt.test(e)||!b.support.leadingWhitespace&&yt.test(e)||At[(bt.exec(e)||["",""])[1].toLowerCase()])){e=e.replace(vt,"<$1></$2>");try{for(;i>r;r++)n=this[r]||{},1===n.nodeType&&(b.cleanData(Ot(n,!1)),n.innerHTML=e);n=0}catch(o){}}n&&this.empty().append(e)},null,e,arguments.length)},replaceWith:function(e){var t=b.isFunction(e);return t||"string"==typeof e||(e=b(e).not(this).detach()),this.domManip([e],!0,function(e){var t=this.nextSibling,n=this.parentNode;n&&(b(this).remove(),n.insertBefore(e,t))})},detach:function(e){return this.remove(e,!0)},domManip:function(e,n,r){e=f.apply([],e);var i,o,a,s,u,l,c=0,p=this.length,d=this,h=p-1,g=e[0],m=b.isFunction(g);if(m||!(1>=p||"string"!=typeof g||b.support.checkClone)&&Ct.test(g))return this.each(function(i){var o=d.eq(i);m&&(e[0]=g.call(this,i,n?o.html():t)),o.domManip(e,n,r)});if(p&&(l=b.buildFragment(e,this[0].ownerDocument,!1,this),i=l.firstChild,1===l.childNodes.length&&(l=i),i)){for(n=n&&b.nodeName(i,"tr"),s=b.map(Ot(l,"script"),Ht),a=s.length;p>c;c++)o=l,c!==h&&(o=b.clone(o,!0,!0),a&&b.merge(s,Ot(o,"script"))),r.call(n&&b.nodeName(this[c],"table")?Lt(this[c],"tbody"):this[c],o,c);if(a)for(u=s[s.length-1].ownerDocument,b.map(s,qt),c=0;a>c;c++)o=s[c],kt.test(o.type||"")&&!b._data(o,"globalEval")&&b.contains(u,o)&&(o.src?b.ajax({url:o.src,type:"GET",dataType:"script",async:!1,global:!1,"throws":!0}):b.globalEval((o.text||o.textContent||o.innerHTML||"").replace(St,"")));l=i=null}return this}});function Lt(e,t){return e.getElementsByTagName(t)[0]||e.appendChild(e.ownerDocument.createElement(t))}function Ht(e){var t=e.getAttributeNode("type");return e.type=(t&&t.specified)+"/"+e.type,e}function qt(e){var t=Et.exec(e.type);return t?e.type=t[1]:e.removeAttribute("type"),e}function Mt(e,t){var n,r=0;for(;null!=(n=e[r]);r++)b._data(n,"globalEval",!t||b._data(t[r],"globalEval"))}function _t(e,t){if(1===t.nodeType&&b.hasData(e)){var n,r,i,o=b._data(e),a=b._data(t,o),s=o.events;if(s){delete a.handle,a.events={};for(n in s)for(r=0,i=s[n].length;i>r;r++)b.event.add(t,n,s[n][r])}a.data&&(a.data=b.extend({},a.data))}}function Ft(e,t){var n,r,i;if(1===t.nodeType){if(n=t.nodeName.toLowerCase(),!b.support.noCloneEvent&&t[b.expando]){i=b._data(t);for(r in i.events)b.removeEvent(t,r,i.handle);t.removeAttribute(b.expando)}"script"===n&&t.text!==e.text?(Ht(t).text=e.text,qt(t)):"object"===n?(t.parentNode&&(t.outerHTML=e.outerHTML),b.support.html5Clone&&e.innerHTML&&!b.trim(t.innerHTML)&&(t.innerHTML=e.innerHTML)):"input"===n&&Nt.test(e.type)?(t.defaultChecked=t.checked=e.checked,t.value!==e.value&&(t.value=e.value)):"option"===n?t.defaultSelected=t.selected=e.defaultSelected:("input"===n||"textarea"===n)&&(t.defaultValue=e.defaultValue)}}b.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(e,t){b.fn[e]=function(e){var n,r=0,i=[],o=b(e),a=o.length-1;for(;a>=r;r++)n=r===a?this:this.clone(!0),b(o[r])[t](n),d.apply(i,n.get());return this.pushStack(i)}});function Ot(e,n){var r,o,a=0,s=typeof e.getElementsByTagName!==i?e.getElementsByTagName(n||"*"):typeof e.querySelectorAll!==i?e.querySelectorAll(n||"*"):t;if(!s)for(s=[],r=e.childNodes||e;null!=(o=r[a]);a++)!n||b.nodeName(o,n)?s.push(o):b.merge(s,Ot(o,n));return n===t||n&&b.nodeName(e,n)?b.merge([e],s):s}function Bt(e){Nt.test(e.type)&&(e.defaultChecked=e.checked)}b.extend({clone:function(e,t,n){var r,i,o,a,s,u=b.contains(e.ownerDocument,e);if(b.support.html5Clone||b.isXMLDoc(e)||!mt.test("<"+e.nodeName+">")?o=e.cloneNode(!0):(Dt.innerHTML=e.outerHTML,Dt.removeChild(o=Dt.firstChild)),!(b.support.noCloneEvent&&b.support.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||b.isXMLDoc(e)))for(r=Ot(o),s=Ot(e),a=0;null!=(i=s[a]);++a)r[a]&&Ft(i,r[a]);if(t)if(n)for(s=s||Ot(e),r=r||Ot(o),a=0;null!=(i=s[a]);a++)_t(i,r[a]);else _t(e,o);return r=Ot(o,"script"),r.length>0&&Mt(r,!u&&Ot(e,"script")),r=s=i=null,o},buildFragment:function(e,t,n,r){var i,o,a,s,u,l,c,p=e.length,f=dt(t),d=[],h=0;for(;p>h;h++)if(o=e[h],o||0===o)if("object"===b.type(o))b.merge(d,o.nodeType?[o]:o);else if(wt.test(o)){s=s||f.appendChild(t.createElement("div")),u=(bt.exec(o)||["",""])[1].toLowerCase(),c=At[u]||At._default,s.innerHTML=c[1]+o.replace(vt,"<$1></$2>")+c[2],i=c[0];while(i--)s=s.lastChild;if(!b.support.leadingWhitespace&&yt.test(o)&&d.push(t.createTextNode(yt.exec(o)[0])),!b.support.tbody){o="table"!==u||xt.test(o)?"<table>"!==c[1]||xt.test(o)?0:s:s.firstChild,i=o&&o.childNodes.length;while(i--)b.nodeName(l=o.childNodes[i],"tbody")&&!l.childNodes.length&&o.removeChild(l)
-}b.merge(d,s.childNodes),s.textContent="";while(s.firstChild)s.removeChild(s.firstChild);s=f.lastChild}else d.push(t.createTextNode(o));s&&f.removeChild(s),b.support.appendChecked||b.grep(Ot(d,"input"),Bt),h=0;while(o=d[h++])if((!r||-1===b.inArray(o,r))&&(a=b.contains(o.ownerDocument,o),s=Ot(f.appendChild(o),"script"),a&&Mt(s),n)){i=0;while(o=s[i++])kt.test(o.type||"")&&n.push(o)}return s=null,f},cleanData:function(e,t){var n,r,o,a,s=0,u=b.expando,l=b.cache,p=b.support.deleteExpando,f=b.event.special;for(;null!=(n=e[s]);s++)if((t||b.acceptData(n))&&(o=n[u],a=o&&l[o])){if(a.events)for(r in a.events)f[r]?b.event.remove(n,r):b.removeEvent(n,r,a.handle);l[o]&&(delete l[o],p?delete n[u]:typeof n.removeAttribute!==i?n.removeAttribute(u):n[u]=null,c.push(o))}}});var Pt,Rt,Wt,$t=/alpha\([^)]*\)/i,It=/opacity\s*=\s*([^)]*)/,zt=/^(top|right|bottom|left)$/,Xt=/^(none|table(?!-c[ea]).+)/,Ut=/^margin/,Vt=RegExp("^("+x+")(.*)$","i"),Yt=RegExp("^("+x+")(?!px)[a-z%]+$","i"),Jt=RegExp("^([+-])=("+x+")","i"),Gt={BODY:"block"},Qt={position:"absolute",visibility:"hidden",display:"block"},Kt={letterSpacing:0,fontWeight:400},Zt=["Top","Right","Bottom","Left"],en=["Webkit","O","Moz","ms"];function tn(e,t){if(t in e)return t;var n=t.charAt(0).toUpperCase()+t.slice(1),r=t,i=en.length;while(i--)if(t=en[i]+n,t in e)return t;return r}function nn(e,t){return e=t||e,"none"===b.css(e,"display")||!b.contains(e.ownerDocument,e)}function rn(e,t){var n,r,i,o=[],a=0,s=e.length;for(;s>a;a++)r=e[a],r.style&&(o[a]=b._data(r,"olddisplay"),n=r.style.display,t?(o[a]||"none"!==n||(r.style.display=""),""===r.style.display&&nn(r)&&(o[a]=b._data(r,"olddisplay",un(r.nodeName)))):o[a]||(i=nn(r),(n&&"none"!==n||!i)&&b._data(r,"olddisplay",i?n:b.css(r,"display"))));for(a=0;s>a;a++)r=e[a],r.style&&(t&&"none"!==r.style.display&&""!==r.style.display||(r.style.display=t?o[a]||"":"none"));return e}b.fn.extend({css:function(e,n){return b.access(this,function(e,n,r){var i,o,a={},s=0;if(b.isArray(n)){for(o=Rt(e),i=n.length;i>s;s++)a[n[s]]=b.css(e,n[s],!1,o);return a}return r!==t?b.style(e,n,r):b.css(e,n)},e,n,arguments.length>1)},show:function(){return rn(this,!0)},hide:function(){return rn(this)},toggle:function(e){var t="boolean"==typeof e;return this.each(function(){(t?e:nn(this))?b(this).show():b(this).hide()})}}),b.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=Wt(e,"opacity");return""===n?"1":n}}}},cssNumber:{columnCount:!0,fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":b.support.cssFloat?"cssFloat":"styleFloat"},style:function(e,n,r,i){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var o,a,s,u=b.camelCase(n),l=e.style;if(n=b.cssProps[u]||(b.cssProps[u]=tn(l,u)),s=b.cssHooks[n]||b.cssHooks[u],r===t)return s&&"get"in s&&(o=s.get(e,!1,i))!==t?o:l[n];if(a=typeof r,"string"===a&&(o=Jt.exec(r))&&(r=(o[1]+1)*o[2]+parseFloat(b.css(e,n)),a="number"),!(null==r||"number"===a&&isNaN(r)||("number"!==a||b.cssNumber[u]||(r+="px"),b.support.clearCloneStyle||""!==r||0!==n.indexOf("background")||(l[n]="inherit"),s&&"set"in s&&(r=s.set(e,r,i))===t)))try{l[n]=r}catch(c){}}},css:function(e,n,r,i){var o,a,s,u=b.camelCase(n);return n=b.cssProps[u]||(b.cssProps[u]=tn(e.style,u)),s=b.cssHooks[n]||b.cssHooks[u],s&&"get"in s&&(a=s.get(e,!0,r)),a===t&&(a=Wt(e,n,i)),"normal"===a&&n in Kt&&(a=Kt[n]),""===r||r?(o=parseFloat(a),r===!0||b.isNumeric(o)?o||0:a):a},swap:function(e,t,n,r){var i,o,a={};for(o in t)a[o]=e.style[o],e.style[o]=t[o];i=n.apply(e,r||[]);for(o in t)e.style[o]=a[o];return i}}),e.getComputedStyle?(Rt=function(t){return e.getComputedStyle(t,null)},Wt=function(e,n,r){var i,o,a,s=r||Rt(e),u=s?s.getPropertyValue(n)||s[n]:t,l=e.style;return s&&(""!==u||b.contains(e.ownerDocument,e)||(u=b.style(e,n)),Yt.test(u)&&Ut.test(n)&&(i=l.width,o=l.minWidth,a=l.maxWidth,l.minWidth=l.maxWidth=l.width=u,u=s.width,l.width=i,l.minWidth=o,l.maxWidth=a)),u}):o.documentElement.currentStyle&&(Rt=function(e){return e.currentStyle},Wt=function(e,n,r){var i,o,a,s=r||Rt(e),u=s?s[n]:t,l=e.style;return null==u&&l&&l[n]&&(u=l[n]),Yt.test(u)&&!zt.test(n)&&(i=l.left,o=e.runtimeStyle,a=o&&o.left,a&&(o.left=e.currentStyle.left),l.left="fontSize"===n?"1em":u,u=l.pixelLeft+"px",l.left=i,a&&(o.left=a)),""===u?"auto":u});function on(e,t,n){var r=Vt.exec(t);return r?Math.max(0,r[1]-(n||0))+(r[2]||"px"):t}function an(e,t,n,r,i){var o=n===(r?"border":"content")?4:"width"===t?1:0,a=0;for(;4>o;o+=2)"margin"===n&&(a+=b.css(e,n+Zt[o],!0,i)),r?("content"===n&&(a-=b.css(e,"padding"+Zt[o],!0,i)),"margin"!==n&&(a-=b.css(e,"border"+Zt[o]+"Width",!0,i))):(a+=b.css(e,"padding"+Zt[o],!0,i),"padding"!==n&&(a+=b.css(e,"border"+Zt[o]+"Width",!0,i)));return a}function sn(e,t,n){var r=!0,i="width"===t?e.offsetWidth:e.offsetHeight,o=Rt(e),a=b.support.boxSizing&&"border-box"===b.css(e,"boxSizing",!1,o);if(0>=i||null==i){if(i=Wt(e,t,o),(0>i||null==i)&&(i=e.style[t]),Yt.test(i))return i;r=a&&(b.support.boxSizingReliable||i===e.style[t]),i=parseFloat(i)||0}return i+an(e,t,n||(a?"border":"content"),r,o)+"px"}function un(e){var t=o,n=Gt[e];return n||(n=ln(e,t),"none"!==n&&n||(Pt=(Pt||b("<iframe frameborder='0' width='0' height='0'/>").css("cssText","display:block !important")).appendTo(t.documentElement),t=(Pt[0].contentWindow||Pt[0].contentDocument).document,t.write("<!doctype html><html><body>"),t.close(),n=ln(e,t),Pt.detach()),Gt[e]=n),n}function ln(e,t){var n=b(t.createElement(e)).appendTo(t.body),r=b.css(n[0],"display");return n.remove(),r}b.each(["height","width"],function(e,n){b.cssHooks[n]={get:function(e,r,i){return r?0===e.offsetWidth&&Xt.test(b.css(e,"display"))?b.swap(e,Qt,function(){return sn(e,n,i)}):sn(e,n,i):t},set:function(e,t,r){var i=r&&Rt(e);return on(e,t,r?an(e,n,r,b.support.boxSizing&&"border-box"===b.css(e,"boxSizing",!1,i),i):0)}}}),b.support.opacity||(b.cssHooks.opacity={get:function(e,t){return It.test((t&&e.currentStyle?e.currentStyle.filter:e.style.filter)||"")?.01*parseFloat(RegExp.$1)+"":t?"1":""},set:function(e,t){var n=e.style,r=e.currentStyle,i=b.isNumeric(t)?"alpha(opacity="+100*t+")":"",o=r&&r.filter||n.filter||"";n.zoom=1,(t>=1||""===t)&&""===b.trim(o.replace($t,""))&&n.removeAttribute&&(n.removeAttribute("filter"),""===t||r&&!r.filter)||(n.filter=$t.test(o)?o.replace($t,i):o+" "+i)}}),b(function(){b.support.reliableMarginRight||(b.cssHooks.marginRight={get:function(e,n){return n?b.swap(e,{display:"inline-block"},Wt,[e,"marginRight"]):t}}),!b.support.pixelPosition&&b.fn.position&&b.each(["top","left"],function(e,n){b.cssHooks[n]={get:function(e,r){return r?(r=Wt(e,n),Yt.test(r)?b(e).position()[n]+"px":r):t}}})}),b.expr&&b.expr.filters&&(b.expr.filters.hidden=function(e){return 0>=e.offsetWidth&&0>=e.offsetHeight||!b.support.reliableHiddenOffsets&&"none"===(e.style&&e.style.display||b.css(e,"display"))},b.expr.filters.visible=function(e){return!b.expr.filters.hidden(e)}),b.each({margin:"",padding:"",border:"Width"},function(e,t){b.cssHooks[e+t]={expand:function(n){var r=0,i={},o="string"==typeof n?n.split(" "):[n];for(;4>r;r++)i[e+Zt[r]+t]=o[r]||o[r-2]||o[0];return i}},Ut.test(e)||(b.cssHooks[e+t].set=on)});var cn=/%20/g,pn=/\[\]$/,fn=/\r?\n/g,dn=/^(?:submit|button|image|reset|file)$/i,hn=/^(?:input|select|textarea|keygen)/i;b.fn.extend({serialize:function(){return b.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var e=b.prop(this,"elements");return e?b.makeArray(e):this}).filter(function(){var e=this.type;return this.name&&!b(this).is(":disabled")&&hn.test(this.nodeName)&&!dn.test(e)&&(this.checked||!Nt.test(e))}).map(function(e,t){var n=b(this).val();return null==n?null:b.isArray(n)?b.map(n,function(e){return{name:t.name,value:e.replace(fn,"\r\n")}}):{name:t.name,value:n.replace(fn,"\r\n")}}).get()}}),b.param=function(e,n){var r,i=[],o=function(e,t){t=b.isFunction(t)?t():null==t?"":t,i[i.length]=encodeURIComponent(e)+"="+encodeURIComponent(t)};if(n===t&&(n=b.ajaxSettings&&b.ajaxSettings.traditional),b.isArray(e)||e.jquery&&!b.isPlainObject(e))b.each(e,function(){o(this.name,this.value)});else for(r in e)gn(r,e[r],n,o);return i.join("&").replace(cn,"+")};function gn(e,t,n,r){var i;if(b.isArray(t))b.each(t,function(t,i){n||pn.test(e)?r(e,i):gn(e+"["+("object"==typeof i?t:"")+"]",i,n,r)});else if(n||"object"!==b.type(t))r(e,t);else for(i in t)gn(e+"["+i+"]",t[i],n,r)}b.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error contextmenu".split(" "),function(e,t){b.fn[t]=function(e,n){return arguments.length>0?this.on(t,null,e,n):this.trigger(t)}}),b.fn.hover=function(e,t){return this.mouseenter(e).mouseleave(t||e)};var mn,yn,vn=b.now(),bn=/\?/,xn=/#.*$/,wn=/([?&])_=[^&]*/,Tn=/^(.*?):[ \t]*([^\r\n]*)\r?$/gm,Nn=/^(?:about|app|app-storage|.+-extension|file|res|widget):$/,Cn=/^(?:GET|HEAD)$/,kn=/^\/\//,En=/^([\w.+-]+:)(?:\/\/([^\/?#:]*)(?::(\d+)|)|)/,Sn=b.fn.load,An={},jn={},Dn="*/".concat("*");try{yn=a.href}catch(Ln){yn=o.createElement("a"),yn.href="",yn=yn.href}mn=En.exec(yn.toLowerCase())||[];function Hn(e){return function(t,n){"string"!=typeof t&&(n=t,t="*");var r,i=0,o=t.toLowerCase().match(w)||[];if(b.isFunction(n))while(r=o[i++])"+"===r[0]?(r=r.slice(1)||"*",(e[r]=e[r]||[]).unshift(n)):(e[r]=e[r]||[]).push(n)}}function qn(e,n,r,i){var o={},a=e===jn;function s(u){var l;return o[u]=!0,b.each(e[u]||[],function(e,u){var c=u(n,r,i);return"string"!=typeof c||a||o[c]?a?!(l=c):t:(n.dataTypes.unshift(c),s(c),!1)}),l}return s(n.dataTypes[0])||!o["*"]&&s("*")}function Mn(e,n){var r,i,o=b.ajaxSettings.flatOptions||{};for(i in n)n[i]!==t&&((o[i]?e:r||(r={}))[i]=n[i]);return r&&b.extend(!0,e,r),e}b.fn.load=function(e,n,r){if("string"!=typeof e&&Sn)return Sn.apply(this,arguments);var i,o,a,s=this,u=e.indexOf(" ");return u>=0&&(i=e.slice(u,e.length),e=e.slice(0,u)),b.isFunction(n)?(r=n,n=t):n&&"object"==typeof n&&(a="POST"),s.length>0&&b.ajax({url:e,type:a,dataType:"html",data:n}).done(function(e){o=arguments,s.html(i?b("<div>").append(b.parseHTML(e)).find(i):e)}).complete(r&&function(e,t){s.each(r,o||[e.responseText,t,e])}),this},b.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){b.fn[t]=function(e){return this.on(t,e)}}),b.each(["get","post"],function(e,n){b[n]=function(e,r,i,o){return b.isFunction(r)&&(o=o||i,i=r,r=t),b.ajax({url:e,type:n,dataType:o,data:r,success:i})}}),b.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:yn,type:"GET",isLocal:Nn.test(mn[1]),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":Dn,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText"},converters:{"* text":e.String,"text html":!0,"text json":b.parseJSON,"text xml":b.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(e,t){return t?Mn(Mn(e,b.ajaxSettings),t):Mn(b.ajaxSettings,e)},ajaxPrefilter:Hn(An),ajaxTransport:Hn(jn),ajax:function(e,n){"object"==typeof e&&(n=e,e=t),n=n||{};var r,i,o,a,s,u,l,c,p=b.ajaxSetup({},n),f=p.context||p,d=p.context&&(f.nodeType||f.jquery)?b(f):b.event,h=b.Deferred(),g=b.Callbacks("once memory"),m=p.statusCode||{},y={},v={},x=0,T="canceled",N={readyState:0,getResponseHeader:function(e){var t;if(2===x){if(!c){c={};while(t=Tn.exec(a))c[t[1].toLowerCase()]=t[2]}t=c[e.toLowerCase()]}return null==t?null:t},getAllResponseHeaders:function(){return 2===x?a:null},setRequestHeader:function(e,t){var n=e.toLowerCase();return x||(e=v[n]=v[n]||e,y[e]=t),this},overrideMimeType:function(e){return x||(p.mimeType=e),this},statusCode:function(e){var t;if(e)if(2>x)for(t in e)m[t]=[m[t],e[t]];else N.always(e[N.status]);return this},abort:function(e){var t=e||T;return l&&l.abort(t),k(0,t),this}};if(h.promise(N).complete=g.add,N.success=N.done,N.error=N.fail,p.url=((e||p.url||yn)+"").replace(xn,"").replace(kn,mn[1]+"//"),p.type=n.method||n.type||p.method||p.type,p.dataTypes=b.trim(p.dataType||"*").toLowerCase().match(w)||[""],null==p.crossDomain&&(r=En.exec(p.url.toLowerCase()),p.crossDomain=!(!r||r[1]===mn[1]&&r[2]===mn[2]&&(r[3]||("http:"===r[1]?80:443))==(mn[3]||("http:"===mn[1]?80:443)))),p.data&&p.processData&&"string"!=typeof p.data&&(p.data=b.param(p.data,p.traditional)),qn(An,p,n,N),2===x)return N;u=p.global,u&&0===b.active++&&b.event.trigger("ajaxStart"),p.type=p.type.toUpperCase(),p.hasContent=!Cn.test(p.type),o=p.url,p.hasContent||(p.data&&(o=p.url+=(bn.test(o)?"&":"?")+p.data,delete p.data),p.cache===!1&&(p.url=wn.test(o)?o.replace(wn,"$1_="+vn++):o+(bn.test(o)?"&":"?")+"_="+vn++)),p.ifModified&&(b.lastModified[o]&&N.setRequestHeader("If-Modified-Since",b.lastModified[o]),b.etag[o]&&N.setRequestHeader("If-None-Match",b.etag[o])),(p.data&&p.hasContent&&p.contentType!==!1||n.contentType)&&N.setRequestHeader("Content-Type",p.contentType),N.setRequestHeader("Accept",p.dataTypes[0]&&p.accepts[p.dataTypes[0]]?p.accepts[p.dataTypes[0]]+("*"!==p.dataTypes[0]?", "+Dn+"; q=0.01":""):p.accepts["*"]);for(i in p.headers)N.setRequestHeader(i,p.headers[i]);if(p.beforeSend&&(p.beforeSend.call(f,N,p)===!1||2===x))return N.abort();T="abort";for(i in{success:1,error:1,complete:1})N[i](p[i]);if(l=qn(jn,p,n,N)){N.readyState=1,u&&d.trigger("ajaxSend",[N,p]),p.async&&p.timeout>0&&(s=setTimeout(function(){N.abort("timeout")},p.timeout));try{x=1,l.send(y,k)}catch(C){if(!(2>x))throw C;k(-1,C)}}else k(-1,"No Transport");function k(e,n,r,i){var c,y,v,w,T,C=n;2!==x&&(x=2,s&&clearTimeout(s),l=t,a=i||"",N.readyState=e>0?4:0,r&&(w=_n(p,N,r)),e>=200&&300>e||304===e?(p.ifModified&&(T=N.getResponseHeader("Last-Modified"),T&&(b.lastModified[o]=T),T=N.getResponseHeader("etag"),T&&(b.etag[o]=T)),204===e?(c=!0,C="nocontent"):304===e?(c=!0,C="notmodified"):(c=Fn(p,w),C=c.state,y=c.data,v=c.error,c=!v)):(v=C,(e||!C)&&(C="error",0>e&&(e=0))),N.status=e,N.statusText=(n||C)+"",c?h.resolveWith(f,[y,C,N]):h.rejectWith(f,[N,C,v]),N.statusCode(m),m=t,u&&d.trigger(c?"ajaxSuccess":"ajaxError",[N,p,c?y:v]),g.fireWith(f,[N,C]),u&&(d.trigger("ajaxComplete",[N,p]),--b.active||b.event.trigger("ajaxStop")))}return N},getScript:function(e,n){return b.get(e,t,n,"script")},getJSON:function(e,t,n){return b.get(e,t,n,"json")}});function _n(e,n,r){var i,o,a,s,u=e.contents,l=e.dataTypes,c=e.responseFields;for(s in c)s in r&&(n[c[s]]=r[s]);while("*"===l[0])l.shift(),o===t&&(o=e.mimeType||n.getResponseHeader("Content-Type"));if(o)for(s in u)if(u[s]&&u[s].test(o)){l.unshift(s);break}if(l[0]in r)a=l[0];else{for(s in r){if(!l[0]||e.converters[s+" "+l[0]]){a=s;break}i||(i=s)}a=a||i}return a?(a!==l[0]&&l.unshift(a),r[a]):t}function Fn(e,t){var n,r,i,o,a={},s=0,u=e.dataTypes.slice(),l=u[0];if(e.dataFilter&&(t=e.dataFilter(t,e.dataType)),u[1])for(i in e.converters)a[i.toLowerCase()]=e.converters[i];for(;r=u[++s];)if("*"!==r){if("*"!==l&&l!==r){if(i=a[l+" "+r]||a["* "+r],!i)for(n in a)if(o=n.split(" "),o[1]===r&&(i=a[l+" "+o[0]]||a["* "+o[0]])){i===!0?i=a[n]:a[n]!==!0&&(r=o[0],u.splice(s--,0,r));break}if(i!==!0)if(i&&e["throws"])t=i(t);else try{t=i(t)}catch(c){return{state:"parsererror",error:i?c:"No conversion from "+l+" to "+r}}}l=r}return{state:"success",data:t}}b.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/(?:java|ecma)script/},converters:{"text script":function(e){return b.globalEval(e),e}}}),b.ajaxPrefilter("script",function(e){e.cache===t&&(e.cache=!1),e.crossDomain&&(e.type="GET",e.global=!1)}),b.ajaxTransport("script",function(e){if(e.crossDomain){var n,r=o.head||b("head")[0]||o.documentElement;return{send:function(t,i){n=o.createElement("script"),n.async=!0,e.scriptCharset&&(n.charset=e.scriptCharset),n.src=e.url,n.onload=n.onreadystatechange=function(e,t){(t||!n.readyState||/loaded|complete/.test(n.readyState))&&(n.onload=n.onreadystatechange=null,n.parentNode&&n.parentNode.removeChild(n),n=null,t||i(200,"success"))},r.insertBefore(n,r.firstChild)},abort:function(){n&&n.onload(t,!0)}}}});var On=[],Bn=/(=)\?(?=&|$)|\?\?/;b.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=On.pop()||b.expando+"_"+vn++;return this[e]=!0,e}}),b.ajaxPrefilter("json jsonp",function(n,r,i){var o,a,s,u=n.jsonp!==!1&&(Bn.test(n.url)?"url":"string"==typeof n.data&&!(n.contentType||"").indexOf("application/x-www-form-urlencoded")&&Bn.test(n.data)&&"data");return u||"jsonp"===n.dataTypes[0]?(o=n.jsonpCallback=b.isFunction(n.jsonpCallback)?n.jsonpCallback():n.jsonpCallback,u?n[u]=n[u].replace(Bn,"$1"+o):n.jsonp!==!1&&(n.url+=(bn.test(n.url)?"&":"?")+n.jsonp+"="+o),n.converters["script json"]=function(){return s||b.error(o+" was not called"),s[0]},n.dataTypes[0]="json",a=e[o],e[o]=function(){s=arguments},i.always(function(){e[o]=a,n[o]&&(n.jsonpCallback=r.jsonpCallback,On.push(o)),s&&b.isFunction(a)&&a(s[0]),s=a=t}),"script"):t});var Pn,Rn,Wn=0,$n=e.ActiveXObject&&function(){var e;for(e in Pn)Pn[e](t,!0)};function In(){try{return new e.XMLHttpRequest}catch(t){}}function zn(){try{return new e.ActiveXObject("Microsoft.XMLHTTP")}catch(t){}}b.ajaxSettings.xhr=e.ActiveXObject?function(){return!this.isLocal&&In()||zn()}:In,Rn=b.ajaxSettings.xhr(),b.support.cors=!!Rn&&"withCredentials"in Rn,Rn=b.support.ajax=!!Rn,Rn&&b.ajaxTransport(function(n){if(!n.crossDomain||b.support.cors){var r;return{send:function(i,o){var a,s,u=n.xhr();if(n.username?u.open(n.type,n.url,n.async,n.username,n.password):u.open(n.type,n.url,n.async),n.xhrFields)for(s in n.xhrFields)u[s]=n.xhrFields[s];n.mimeType&&u.overrideMimeType&&u.overrideMimeType(n.mimeType),n.crossDomain||i["X-Requested-With"]||(i["X-Requested-With"]="XMLHttpRequest");try{for(s in i)u.setRequestHeader(s,i[s])}catch(l){}u.send(n.hasContent&&n.data||null),r=function(e,i){var s,l,c,p;try{if(r&&(i||4===u.readyState))if(r=t,a&&(u.onreadystatechange=b.noop,$n&&delete Pn[a]),i)4!==u.readyState&&u.abort();else{p={},s=u.status,l=u.getAllResponseHeaders(),"string"==typeof u.responseText&&(p.text=u.responseText);try{c=u.statusText}catch(f){c=""}s||!n.isLocal||n.crossDomain?1223===s&&(s=204):s=p.text?200:404}}catch(d){i||o(-1,d)}p&&o(s,c,p,l)},n.async?4===u.readyState?setTimeout(r):(a=++Wn,$n&&(Pn||(Pn={},b(e).unload($n)),Pn[a]=r),u.onreadystatechange=r):r()},abort:function(){r&&r(t,!0)}}}});var Xn,Un,Vn=/^(?:toggle|show|hide)$/,Yn=RegExp("^(?:([+-])=|)("+x+")([a-z%]*)$","i"),Jn=/queueHooks$/,Gn=[nr],Qn={"*":[function(e,t){var n,r,i=this.createTween(e,t),o=Yn.exec(t),a=i.cur(),s=+a||0,u=1,l=20;if(o){if(n=+o[2],r=o[3]||(b.cssNumber[e]?"":"px"),"px"!==r&&s){s=b.css(i.elem,e,!0)||n||1;do u=u||".5",s/=u,b.style(i.elem,e,s+r);while(u!==(u=i.cur()/a)&&1!==u&&--l)}i.unit=r,i.start=s,i.end=o[1]?s+(o[1]+1)*n:n}return i}]};function Kn(){return setTimeout(function(){Xn=t}),Xn=b.now()}function Zn(e,t){b.each(t,function(t,n){var r=(Qn[t]||[]).concat(Qn["*"]),i=0,o=r.length;for(;o>i;i++)if(r[i].call(e,t,n))return})}function er(e,t,n){var r,i,o=0,a=Gn.length,s=b.Deferred().always(function(){delete u.elem}),u=function(){if(i)return!1;var t=Xn||Kn(),n=Math.max(0,l.startTime+l.duration-t),r=n/l.duration||0,o=1-r,a=0,u=l.tweens.length;for(;u>a;a++)l.tweens[a].run(o);return s.notifyWith(e,[l,o,n]),1>o&&u?n:(s.resolveWith(e,[l]),!1)},l=s.promise({elem:e,props:b.extend({},t),opts:b.extend(!0,{specialEasing:{}},n),originalProperties:t,originalOptions:n,startTime:Xn||Kn(),duration:n.duration,tweens:[],createTween:function(t,n){var r=b.Tween(e,l.opts,t,n,l.opts.specialEasing[t]||l.opts.easing);return l.tweens.push(r),r},stop:function(t){var n=0,r=t?l.tweens.length:0;if(i)return this;for(i=!0;r>n;n++)l.tweens[n].run(1);return t?s.resolveWith(e,[l,t]):s.rejectWith(e,[l,t]),this}}),c=l.props;for(tr(c,l.opts.specialEasing);a>o;o++)if(r=Gn[o].call(l,e,c,l.opts))return r;return Zn(l,c),b.isFunction(l.opts.start)&&l.opts.start.call(e,l),b.fx.timer(b.extend(u,{elem:e,anim:l,queue:l.opts.queue})),l.progress(l.opts.progress).done(l.opts.done,l.opts.complete).fail(l.opts.fail).always(l.opts.always)}function tr(e,t){var n,r,i,o,a;for(i in e)if(r=b.camelCase(i),o=t[r],n=e[i],b.isArray(n)&&(o=n[1],n=e[i]=n[0]),i!==r&&(e[r]=n,delete e[i]),a=b.cssHooks[r],a&&"expand"in a){n=a.expand(n),delete e[r];for(i in n)i in e||(e[i]=n[i],t[i]=o)}else t[r]=o}b.Animation=b.extend(er,{tweener:function(e,t){b.isFunction(e)?(t=e,e=["*"]):e=e.split(" ");var n,r=0,i=e.length;for(;i>r;r++)n=e[r],Qn[n]=Qn[n]||[],Qn[n].unshift(t)},prefilter:function(e,t){t?Gn.unshift(e):Gn.push(e)}});function nr(e,t,n){var r,i,o,a,s,u,l,c,p,f=this,d=e.style,h={},g=[],m=e.nodeType&&nn(e);n.queue||(c=b._queueHooks(e,"fx"),null==c.unqueued&&(c.unqueued=0,p=c.empty.fire,c.empty.fire=function(){c.unqueued||p()}),c.unqueued++,f.always(function(){f.always(function(){c.unqueued--,b.queue(e,"fx").length||c.empty.fire()})})),1===e.nodeType&&("height"in t||"width"in t)&&(n.overflow=[d.overflow,d.overflowX,d.overflowY],"inline"===b.css(e,"display")&&"none"===b.css(e,"float")&&(b.support.inlineBlockNeedsLayout&&"inline"!==un(e.nodeName)?d.zoom=1:d.display="inline-block")),n.overflow&&(d.overflow="hidden",b.support.shrinkWrapBlocks||f.always(function(){d.overflow=n.overflow[0],d.overflowX=n.overflow[1],d.overflowY=n.overflow[2]}));for(i in t)if(a=t[i],Vn.exec(a)){if(delete t[i],u=u||"toggle"===a,a===(m?"hide":"show"))continue;g.push(i)}if(o=g.length){s=b._data(e,"fxshow")||b._data(e,"fxshow",{}),"hidden"in s&&(m=s.hidden),u&&(s.hidden=!m),m?b(e).show():f.done(function(){b(e).hide()}),f.done(function(){var t;b._removeData(e,"fxshow");for(t in h)b.style(e,t,h[t])});for(i=0;o>i;i++)r=g[i],l=f.createTween(r,m?s[r]:0),h[r]=s[r]||b.style(e,r),r in s||(s[r]=l.start,m&&(l.end=l.start,l.start="width"===r||"height"===r?1:0))}}function rr(e,t,n,r,i){return new rr.prototype.init(e,t,n,r,i)}b.Tween=rr,rr.prototype={constructor:rr,init:function(e,t,n,r,i,o){this.elem=e,this.prop=n,this.easing=i||"swing",this.options=t,this.start=this.now=this.cur(),this.end=r,this.unit=o||(b.cssNumber[n]?"":"px")},cur:function(){var e=rr.propHooks[this.prop];return e&&e.get?e.get(this):rr.propHooks._default.get(this)},run:function(e){var t,n=rr.propHooks[this.prop];return this.pos=t=this.options.duration?b.easing[this.easing](e,this.options.duration*e,0,1,this.options.duration):e,this.now=(this.end-this.start)*t+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),n&&n.set?n.set(this):rr.propHooks._default.set(this),this}},rr.prototype.init.prototype=rr.prototype,rr.propHooks={_default:{get:function(e){var t;return null==e.elem[e.prop]||e.elem.style&&null!=e.elem.style[e.prop]?(t=b.css(e.elem,e.prop,""),t&&"auto"!==t?t:0):e.elem[e.prop]},set:function(e){b.fx.step[e.prop]?b.fx.step[e.prop](e):e.elem.style&&(null!=e.elem.style[b.cssProps[e.prop]]||b.cssHooks[e.prop])?b.style(e.elem,e.prop,e.now+e.unit):e.elem[e.prop]=e.now}}},rr.propHooks.scrollTop=rr.propHooks.scrollLeft={set:function(e){e.elem.nodeType&&e.elem.parentNode&&(e.elem[e.prop]=e.now)}},b.each(["toggle","show","hide"],function(e,t){var n=b.fn[t];b.fn[t]=function(e,r,i){return null==e||"boolean"==typeof e?n.apply(this,arguments):this.animate(ir(t,!0),e,r,i)}}),b.fn.extend({fadeTo:function(e,t,n,r){return this.filter(nn).css("opacity",0).show().end().animate({opacity:t},e,n,r)},animate:function(e,t,n,r){var i=b.isEmptyObject(e),o=b.speed(t,n,r),a=function(){var t=er(this,b.extend({},e),o);a.finish=function(){t.stop(!0)},(i||b._data(this,"finish"))&&t.stop(!0)};return a.finish=a,i||o.queue===!1?this.each(a):this.queue(o.queue,a)},stop:function(e,n,r){var i=function(e){var t=e.stop;delete e.stop,t(r)};return"string"!=typeof e&&(r=n,n=e,e=t),n&&e!==!1&&this.queue(e||"fx",[]),this.each(function(){var t=!0,n=null!=e&&e+"queueHooks",o=b.timers,a=b._data(this);if(n)a[n]&&a[n].stop&&i(a[n]);else for(n in a)a[n]&&a[n].stop&&Jn.test(n)&&i(a[n]);for(n=o.length;n--;)o[n].elem!==this||null!=e&&o[n].queue!==e||(o[n].anim.stop(r),t=!1,o.splice(n,1));(t||!r)&&b.dequeue(this,e)})},finish:function(e){return e!==!1&&(e=e||"fx"),this.each(function(){var t,n=b._data(this),r=n[e+"queue"],i=n[e+"queueHooks"],o=b.timers,a=r?r.length:0;for(n.finish=!0,b.queue(this,e,[]),i&&i.cur&&i.cur.finish&&i.cur.finish.call(this),t=o.length;t--;)o[t].elem===this&&o[t].queue===e&&(o[t].anim.stop(!0),o.splice(t,1));for(t=0;a>t;t++)r[t]&&r[t].finish&&r[t].finish.call(this);delete n.finish})}});function ir(e,t){var n,r={height:e},i=0;for(t=t?1:0;4>i;i+=2-t)n=Zt[i],r["margin"+n]=r["padding"+n]=e;return t&&(r.opacity=r.width=e),r}b.each({slideDown:ir("show"),slideUp:ir("hide"),slideToggle:ir("toggle"),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(e,t){b.fn[e]=function(e,n,r){return this.animate(t,e,n,r)}}),b.speed=function(e,t,n){var r=e&&"object"==typeof e?b.extend({},e):{complete:n||!n&&t||b.isFunction(e)&&e,duration:e,easing:n&&t||t&&!b.isFunction(t)&&t};return r.duration=b.fx.off?0:"number"==typeof r.duration?r.duration:r.duration in b.fx.speeds?b.fx.speeds[r.duration]:b.fx.speeds._default,(null==r.queue||r.queue===!0)&&(r.queue="fx"),r.old=r.complete,r.complete=function(){b.isFunction(r.old)&&r.old.call(this),r.queue&&b.dequeue(this,r.queue)},r},b.easing={linear:function(e){return e},swing:function(e){return.5-Math.cos(e*Math.PI)/2}},b.timers=[],b.fx=rr.prototype.init,b.fx.tick=function(){var e,n=b.timers,r=0;for(Xn=b.now();n.length>r;r++)e=n[r],e()||n[r]!==e||n.splice(r--,1);n.length||b.fx.stop(),Xn=t},b.fx.timer=function(e){e()&&b.timers.push(e)&&b.fx.start()},b.fx.interval=13,b.fx.start=function(){Un||(Un=setInterval(b.fx.tick,b.fx.interval))},b.fx.stop=function(){clearInterval(Un),Un=null},b.fx.speeds={slow:600,fast:200,_default:400},b.fx.step={},b.expr&&b.expr.filters&&(b.expr.filters.animated=function(e){return b.grep(b.timers,function(t){return e===t.elem}).length}),b.fn.offset=function(e){if(arguments.length)return e===t?this:this.each(function(t){b.offset.setOffset(this,e,t)});var n,r,o={top:0,left:0},a=this[0],s=a&&a.ownerDocument;if(s)return n=s.documentElement,b.contains(n,a)?(typeof a.getBoundingClientRect!==i&&(o=a.getBoundingClientRect()),r=or(s),{top:o.top+(r.pageYOffset||n.scrollTop)-(n.clientTop||0),left:o.left+(r.pageXOffset||n.scrollLeft)-(n.clientLeft||0)}):o},b.offset={setOffset:function(e,t,n){var r=b.css(e,"position");"static"===r&&(e.style.position="relative");var i=b(e),o=i.offset(),a=b.css(e,"top"),s=b.css(e,"left"),u=("absolute"===r||"fixed"===r)&&b.inArray("auto",[a,s])>-1,l={},c={},p,f;u?(c=i.position(),p=c.top,f=c.left):(p=parseFloat(a)||0,f=parseFloat(s)||0),b.isFunction(t)&&(t=t.call(e,n,o)),null!=t.top&&(l.top=t.top-o.top+p),null!=t.left&&(l.left=t.left-o.left+f),"using"in t?t.using.call(e,l):i.css(l)}},b.fn.extend({position:function(){if(this[0]){var e,t,n={top:0,left:0},r=this[0];return"fixed"===b.css(r,"position")?t=r.getBoundingClientRect():(e=this.offsetParent(),t=this.offset(),b.nodeName(e[0],"html")||(n=e.offset()),n.top+=b.css(e[0],"borderTopWidth",!0),n.left+=b.css(e[0],"borderLeftWidth",!0)),{top:t.top-n.top-b.css(r,"marginTop",!0),left:t.left-n.left-b.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent||o.documentElement;while(e&&!b.nodeName(e,"html")&&"static"===b.css(e,"position"))e=e.offsetParent;return e||o.documentElement})}}),b.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(e,n){var r=/Y/.test(n);b.fn[e]=function(i){return b.access(this,function(e,i,o){var a=or(e);return o===t?a?n in a?a[n]:a.document.documentElement[i]:e[i]:(a?a.scrollTo(r?b(a).scrollLeft():o,r?o:b(a).scrollTop()):e[i]=o,t)},e,i,arguments.length,null)}});function or(e){return b.isWindow(e)?e:9===e.nodeType?e.defaultView||e.parentWindow:!1}b.each({Height:"height",Width:"width"},function(e,n){b.each({padding:"inner"+e,content:n,"":"outer"+e},function(r,i){b.fn[i]=function(i,o){var a=arguments.length&&(r||"boolean"!=typeof i),s=r||(i===!0||o===!0?"margin":"border");return b.access(this,function(n,r,i){var o;return b.isWindow(n)?n.document.documentElement["client"+e]:9===n.nodeType?(o=n.documentElement,Math.max(n.body["scroll"+e],o["scroll"+e],n.body["offset"+e],o["offset"+e],o["client"+e])):i===t?b.css(n,r,s):b.style(n,r,i,s)},n,a?i:t,a,null)}})}),e.jQuery=e.$=b,"function"==typeof define&&define.amd&&define.amd.jQuery&&define("jquery",[],function(){return b})})(window);
\ No newline at end of file
+*/
+(function(e,undefined){var t,n,r=typeof undefined,i=e.location,o=e.document,s=o.documentElement,a=e.jQuery,u=e.$,l={},c=[],f="2.0.0",p=c.concat,h=c.push,d=c.slice,g=c.indexOf,m=l.toString,y=l.hasOwnProperty,v=f.trim,x=function(e,n){return new x.fn.init(e,n,t)},b=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,w=/\S+/g,T=/^(?:(<[\w\W]+>)[^>]*|#([\w-]*))$/,C=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,k=/^-ms-/,N=/-([\da-z])/gi,E=function(e,t){return t.toUpperCase()},S=function(){o.removeEventListener("DOMContentLoaded",S,!1),e.removeEventListener("load",S,!1),x.ready()};x.fn=x.prototype={jquery:f,constructor:x,init:function(e,t,n){var r,i;if(!e)return this;if("string"==typeof e){if(r="<"===e.charAt(0)&&">"===e.charAt(e.length-1)&&e.length>=3?[null,e,null]:T.exec(e),!r||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof x?t[0]:t,x.merge(this,x.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:o,!0)),C.test(r[1])&&x.isPlainObject(t))for(r in t)x.isFunction(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return i=o.getElementById(r[2]),i&&i.parentNode&&(this.length=1,this[0]=i),this.context=o,this.selector=e,this}return e.nodeType?(this.context=this[0]=e,this.length=1,this):x.isFunction(e)?n.ready(e):(e.selector!==undefined&&(this.selector=e.selector,this.context=e.context),x.makeArray(e,this))},selector:"",length:0,toArray:function(){return d.call(this)},get:function(e){return null==e?this.toArray():0>e?this[this.length+e]:this[e]},pushStack:function(e){var t=x.merge(this.constructor(),e);return t.prevObject=this,t.context=this.context,t},each:function(e,t){return x.each(this,e,t)},ready:function(e){return x.ready.promise().done(e),this},slice:function(){return this.pushStack(d.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(e){var t=this.length,n=+e+(0>e?t:0);return this.pushStack(n>=0&&t>n?[this[n]]:[])},map:function(e){return this.pushStack(x.map(this,function(t,n){return e.call(t,n,t)}))},end:function(){return this.prevObject||this.constructor(null)},push:h,sort:[].sort,splice:[].splice},x.fn.init.prototype=x.fn,x.extend=x.fn.extend=function(){var e,t,n,r,i,o,s=arguments[0]||{},a=1,u=arguments.length,l=!1;for("boolean"==typeof s&&(l=s,s=arguments[1]||{},a=2),"object"==typeof s||x.isFunction(s)||(s={}),u===a&&(s=this,--a);u>a;a++)if(null!=(e=arguments[a]))for(t in e)n=s[t],r=e[t],s!==r&&(l&&r&&(x.isPlainObject(r)||(i=x.isArray(r)))?(i?(i=!1,o=n&&x.isArray(n)?n:[]):o=n&&x.isPlainObject(n)?n:{},s[t]=x.extend(l,o,r)):r!==undefined&&(s[t]=r));return s},x.extend({expando:"jQuery"+(f+Math.random()).replace(/\D/g,""),noConflict:function(t){return e.$===x&&(e.$=u),t&&e.jQuery===x&&(e.jQuery=a),x},isReady:!1,readyWait:1,holdReady:function(e){e?x.readyWait++:x.ready(!0)},ready:function(e){(e===!0?--x.readyWait:x.isReady)||(x.isReady=!0,e!==!0&&--x.readyWait>0||(n.resolveWith(o,[x]),x.fn.trigger&&x(o).trigger("ready").off("ready")))},isFunction:function(e){return"function"===x.type(e)},isArray:Array.isArray,isWindow:function(e){return null!=e&&e===e.window},isNumeric:function(e){return!isNaN(parseFloat(e))&&isFinite(e)},type:function(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?l[m.call(e)]||"object":typeof e},isPlainObject:function(e){if("object"!==x.type(e)||e.nodeType||x.isWindow(e))return!1;try{if(e.constructor&&!y.call(e.constructor.prototype,"isPrototypeOf"))return!1}catch(t){return!1}return!0},isEmptyObject:function(e){var t;for(t in e)return!1;return!0},error:function(e){throw Error(e)},parseHTML:function(e,t,n){if(!e||"string"!=typeof e)return null;"boolean"==typeof t&&(n=t,t=!1),t=t||o;var r=C.exec(e),i=!n&&[];return r?[t.createElement(r[1])]:(r=x.buildFragment([e],t,i),i&&x(i).remove(),x.merge([],r.childNodes))},parseJSON:JSON.parse,parseXML:function(e){var t,n;if(!e||"string"!=typeof e)return null;try{n=new DOMParser,t=n.parseFromString(e,"text/xml")}catch(r){t=undefined}return(!t||t.getElementsByTagName("parsererror").length)&&x.error("Invalid XML: "+e),t},noop:function(){},globalEval:function(e){var t,n=eval;e=x.trim(e),e&&(1===e.indexOf("use strict")?(t=o.createElement("script"),t.text=e,o.head.appendChild(t).parentNode.removeChild(t)):n(e))},camelCase:function(e){return e.replace(k,"ms-").replace(N,E)},nodeName:function(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()},each:function(e,t,n){var r,i=0,o=e.length,s=j(e);if(n){if(s){for(;o>i;i++)if(r=t.apply(e[i],n),r===!1)break}else for(i in e)if(r=t.apply(e[i],n),r===!1)break}else if(s){for(;o>i;i++)if(r=t.call(e[i],i,e[i]),r===!1)break}else for(i in e)if(r=t.call(e[i],i,e[i]),r===!1)break;return e},trim:function(e){return null==e?"":v.call(e)},makeArray:function(e,t){var n=t||[];return null!=e&&(j(Object(e))?x.merge(n,"string"==typeof e?[e]:e):h.call(n,e)),n},inArray:function(e,t,n){return null==t?-1:g.call(t,e,n)},merge:function(e,t){var n=t.length,r=e.length,i=0;if("number"==typeof n)for(;n>i;i++)e[r++]=t[i];else while(t[i]!==undefined)e[r++]=t[i++];return e.length=r,e},grep:function(e,t,n){var r,i=[],o=0,s=e.length;for(n=!!n;s>o;o++)r=!!t(e[o],o),n!==r&&i.push(e[o]);return i},map:function(e,t,n){var r,i=0,o=e.length,s=j(e),a=[];if(s)for(;o>i;i++)r=t(e[i],i,n),null!=r&&(a[a.length]=r);else for(i in e)r=t(e[i],i,n),null!=r&&(a[a.length]=r);return p.apply([],a)},guid:1,proxy:function(e,t){var n,r,i;return"string"==typeof t&&(n=e[t],t=e,e=n),x.isFunction(e)?(r=d.call(arguments,2),i=function(){return e.apply(t||this,r.concat(d.call(arguments)))},i.guid=e.guid=e.guid||x.guid++,i):undefined},access:function(e,t,n,r,i,o,s){var a=0,u=e.length,l=null==n;if("object"===x.type(n)){i=!0;for(a in n)x.access(e,t,a,n[a],!0,o,s)}else if(r!==undefined&&(i=!0,x.isFunction(r)||(s=!0),l&&(s?(t.call(e,r),t=null):(l=t,t=function(e,t,n){return l.call(x(e),n)})),t))for(;u>a;a++)t(e[a],n,s?r:r.call(e[a],a,t(e[a],n)));return i?e:l?t.call(e):u?t(e[0],n):o},now:Date.now,swap:function(e,t,n,r){var i,o,s={};for(o in t)s[o]=e.style[o],e.style[o]=t[o];i=n.apply(e,r||[]);for(o in t)e.style[o]=s[o];return i}}),x.ready.promise=function(t){return n||(n=x.Deferred(),"complete"===o.readyState?setTimeout(x.ready):(o.addEventListener("DOMContentLoaded",S,!1),e.addEventListener("load",S,!1))),n.promise(t)},x.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(e,t){l["[object "+t+"]"]=t.toLowerCase()});function j(e){var t=e.length,n=x.type(e);return x.isWindow(e)?!1:1===e.nodeType&&t?!0:"array"===n||"function"!==n&&(0===t||"number"==typeof t&&t>0&&t-1 in e)}t=x(o),function(e,undefined){var t,n,r,i,o,s,a,u,l,c,f,p,h,d,g,m,y="sizzle"+-new Date,v=e.document,b={},w=0,T=0,C=ot(),k=ot(),N=ot(),E=!1,S=function(){return 0},j=typeof undefined,D=1<<31,A=[],L=A.pop,q=A.push,H=A.push,O=A.slice,F=A.indexOf||function(e){var t=0,n=this.length;for(;n>t;t++)if(this[t]===e)return t;return-1},P="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",R="[\\x20\\t\\r\\n\\f]",M="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",W=M.replace("w","w#"),$="\\["+R+"*("+M+")"+R+"*(?:([*^$|!~]?=)"+R+"*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|("+W+")|)|)"+R+"*\\]",B=":("+M+")(?:\\(((['\"])((?:\\\\.|[^\\\\])*?)\\3|((?:\\\\.|[^\\\\()[\\]]|"+$.replace(3,8)+")*)|.*)\\)|)",I=RegExp("^"+R+"+|((?:^|[^\\\\])(?:\\\\.)*)"+R+"+$","g"),z=RegExp("^"+R+"*,"+R+"*"),_=RegExp("^"+R+"*([>+~]|"+R+")"+R+"*"),X=RegExp(R+"*[+~]"),U=RegExp("="+R+"*([^\\]'\"]*)"+R+"*\\]","g"),Y=RegExp(B),V=RegExp("^"+W+"$"),G={ID:RegExp("^#("+M+")"),CLASS:RegExp("^\\.("+M+")"),TAG:RegExp("^("+M.replace("w","w*")+")"),ATTR:RegExp("^"+$),PSEUDO:RegExp("^"+B),CHILD:RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+R+"*(even|odd|(([+-]|)(\\d*)n|)"+R+"*(?:([+-]|)"+R+"*(\\d+)|))"+R+"*\\)|)","i"),"boolean":RegExp("^(?:"+P+")$","i"),needsContext:RegExp("^"+R+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+R+"*((?:-\\d)?\\d*)"+R+"*\\)|)(?=[^-]|$)","i")},J=/^[^{]+\{\s*\[native \w/,Q=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,K=/^(?:input|select|textarea|button)$/i,Z=/^h\d$/i,et=/'|\\/g,tt=/\\([\da-fA-F]{1,6}[\x20\t\r\n\f]?|.)/g,nt=function(e,t){var n="0x"+t-65536;return n!==n?t:0>n?String.fromCharCode(n+65536):String.fromCharCode(55296|n>>10,56320|1023&n)};try{H.apply(A=O.call(v.childNodes),v.childNodes),A[v.childNodes.length].nodeType}catch(rt){H={apply:A.length?function(e,t){q.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function it(e){return J.test(e+"")}function ot(){var e,t=[];return e=function(n,i){return t.push(n+=" ")>r.cacheLength&&delete e[t.shift()],e[n]=i}}function st(e){return e[y]=!0,e}function at(e){var t=c.createElement("div");try{return!!e(t)}catch(n){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function ut(e,t,n,r){var i,o,s,a,u,f,d,g,x,w;if((t?t.ownerDocument||t:v)!==c&&l(t),t=t||c,n=n||[],!e||"string"!=typeof e)return n;if(1!==(a=t.nodeType)&&9!==a)return[];if(p&&!r){if(i=Q.exec(e))if(s=i[1]){if(9===a){if(o=t.getElementById(s),!o||!o.parentNode)return n;if(o.id===s)return n.push(o),n}else if(t.ownerDocument&&(o=t.ownerDocument.getElementById(s))&&m(t,o)&&o.id===s)return n.push(o),n}else{if(i[2])return H.apply(n,t.getElementsByTagName(e)),n;if((s=i[3])&&b.getElementsByClassName&&t.getElementsByClassName)return H.apply(n,t.getElementsByClassName(s)),n}if(b.qsa&&(!h||!h.test(e))){if(g=d=y,x=t,w=9===a&&e,1===a&&"object"!==t.nodeName.toLowerCase()){f=gt(e),(d=t.getAttribute("id"))?g=d.replace(et,"\\$&"):t.setAttribute("id",g),g="[id='"+g+"'] ",u=f.length;while(u--)f[u]=g+mt(f[u]);x=X.test(e)&&t.parentNode||t,w=f.join(",")}if(w)try{return H.apply(n,x.querySelectorAll(w)),n}catch(T){}finally{d||t.removeAttribute("id")}}}return kt(e.replace(I,"$1"),t,n,r)}o=ut.isXML=function(e){var t=e&&(e.ownerDocument||e).documentElement;return t?"HTML"!==t.nodeName:!1},l=ut.setDocument=function(e){var t=e?e.ownerDocument||e:v;return t!==c&&9===t.nodeType&&t.documentElement?(c=t,f=t.documentElement,p=!o(t),b.getElementsByTagName=at(function(e){return e.appendChild(t.createComment("")),!e.getElementsByTagName("*").length}),b.attributes=at(function(e){return e.className="i",!e.getAttribute("className")}),b.getElementsByClassName=at(function(e){return e.innerHTML="<div class='a'></div><div class='a i'></div>",e.firstChild.className="i",2===e.getElementsByClassName("i").length}),b.sortDetached=at(function(e){return 1&e.compareDocumentPosition(c.createElement("div"))}),b.getById=at(function(e){return f.appendChild(e).id=y,!t.getElementsByName||!t.getElementsByName(y).length}),b.getById?(r.find.ID=function(e,t){if(typeof t.getElementById!==j&&p){var n=t.getElementById(e);return n&&n.parentNode?[n]:[]}},r.filter.ID=function(e){var t=e.replace(tt,nt);return function(e){return e.getAttribute("id")===t}}):(r.find.ID=function(e,t){if(typeof t.getElementById!==j&&p){var n=t.getElementById(e);return n?n.id===e||typeof n.getAttributeNode!==j&&n.getAttributeNode("id").value===e?[n]:undefined:[]}},r.filter.ID=function(e){var t=e.replace(tt,nt);return function(e){var n=typeof e.getAttributeNode!==j&&e.getAttributeNode("id");return n&&n.value===t}}),r.find.TAG=b.getElementsByTagName?function(e,t){return typeof t.getElementsByTagName!==j?t.getElementsByTagName(e):undefined}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},r.find.CLASS=b.getElementsByClassName&&function(e,t){return typeof t.getElementsByClassName!==j&&p?t.getElementsByClassName(e):undefined},d=[],h=[],(b.qsa=it(t.querySelectorAll))&&(at(function(e){e.innerHTML="<select><option selected=''></option></select>",e.querySelectorAll("[selected]").length||h.push("\\["+R+"*(?:value|"+P+")"),e.querySelectorAll(":checked").length||h.push(":checked")}),at(function(e){var t=c.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("t",""),e.querySelectorAll("[t^='']").length&&h.push("[*^$]="+R+"*(?:''|\"\")"),e.querySelectorAll(":enabled").length||h.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),h.push(",.*:")})),(b.matchesSelector=it(g=f.webkitMatchesSelector||f.mozMatchesSelector||f.oMatchesSelector||f.msMatchesSelector))&&at(function(e){b.disconnectedMatch=g.call(e,"div"),g.call(e,"[s!='']:x"),d.push("!=",B)}),h=h.length&&RegExp(h.join("|")),d=d.length&&RegExp(d.join("|")),m=it(f.contains)||f.compareDocumentPosition?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},S=f.compareDocumentPosition?function(e,n){if(e===n)return E=!0,0;var r=n.compareDocumentPosition&&e.compareDocumentPosition&&e.compareDocumentPosition(n);return r?1&r||!b.sortDetached&&n.compareDocumentPosition(e)===r?e===t||m(v,e)?-1:n===t||m(v,n)?1:u?F.call(u,e)-F.call(u,n):0:4&r?-1:1:e.compareDocumentPosition?-1:1}:function(e,n){var r,i=0,o=e.parentNode,s=n.parentNode,a=[e],l=[n];if(e===n)return E=!0,0;if(!o||!s)return e===t?-1:n===t?1:o?-1:s?1:u?F.call(u,e)-F.call(u,n):0;if(o===s)return lt(e,n);r=e;while(r=r.parentNode)a.unshift(r);r=n;while(r=r.parentNode)l.unshift(r);while(a[i]===l[i])i++;return i?lt(a[i],l[i]):a[i]===v?-1:l[i]===v?1:0},c):c},ut.matches=function(e,t){return ut(e,null,null,t)},ut.matchesSelector=function(e,t){if((e.ownerDocument||e)!==c&&l(e),t=t.replace(U,"='$1']"),!(!b.matchesSelector||!p||d&&d.test(t)||h&&h.test(t)))try{var n=g.call(e,t);if(n||b.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(r){}return ut(t,c,null,[e]).length>0},ut.contains=function(e,t){return(e.ownerDocument||e)!==c&&l(e),m(e,t)},ut.attr=function(e,t){(e.ownerDocument||e)!==c&&l(e);var n=r.attrHandle[t.toLowerCase()],i=n&&n(e,t,!p);return i===undefined?b.attributes||!p?e.getAttribute(t):(i=e.getAttributeNode(t))&&i.specified?i.value:null:i},ut.error=function(e){throw Error("Syntax error, unrecognized expression: "+e)},ut.uniqueSort=function(e){var t,n=[],r=0,i=0;if(E=!b.detectDuplicates,u=!b.sortStable&&e.slice(0),e.sort(S),E){while(t=e[i++])t===e[i]&&(r=n.push(i));while(r--)e.splice(n[r],1)}return e};function lt(e,t){var n=t&&e,r=n&&(~t.sourceIndex||D)-(~e.sourceIndex||D);if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function ct(e,t,n){var r;return n?undefined:(r=e.getAttributeNode(t))&&r.specified?r.value:e[t]===!0?t.toLowerCase():null}function ft(e,t,n){var r;return n?undefined:r=e.getAttribute(t,"type"===t.toLowerCase()?1:2)}function pt(e){return function(t){var n=t.nodeName.toLowerCase();return"input"===n&&t.type===e}}function ht(e){return function(t){var n=t.nodeName.toLowerCase();return("input"===n||"button"===n)&&t.type===e}}function dt(e){return st(function(t){return t=+t,st(function(n,r){var i,o=e([],n.length,t),s=o.length;while(s--)n[i=o[s]]&&(n[i]=!(r[i]=n[i]))})})}i=ut.getText=function(e){var t,n="",r=0,o=e.nodeType;if(o){if(1===o||9===o||11===o){if("string"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=i(e)}else if(3===o||4===o)return e.nodeValue}else for(;t=e[r];r++)n+=i(t);return n},r=ut.selectors={cacheLength:50,createPseudo:st,match:G,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(tt,nt),e[3]=(e[4]||e[5]||"").replace(tt,nt),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||ut.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&ut.error(e[0]),e},PSEUDO:function(e){var t,n=!e[5]&&e[2];return G.CHILD.test(e[0])?null:(e[4]?e[2]=e[4]:n&&Y.test(n)&&(t=gt(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(tt,nt).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=C[e+" "];return t||(t=RegExp("(^|"+R+")"+e+"("+R+"|$)"))&&C(e,function(e){return t.test("string"==typeof e.className&&e.className||typeof e.getAttribute!==j&&e.getAttribute("class")||"")})},ATTR:function(e,t,n){return function(r){var i=ut.attr(r,e);return null==i?"!="===t:t?(i+="","="===t?i===n:"!="===t?i!==n:"^="===t?n&&0===i.indexOf(n):"*="===t?n&&i.indexOf(n)>-1:"$="===t?n&&i.slice(-n.length)===n:"~="===t?(" "+i+" ").indexOf(n)>-1:"|="===t?i===n||i.slice(0,n.length+1)===n+"-":!1):!0}},CHILD:function(e,t,n,r,i){var o="nth"!==e.slice(0,3),s="last"!==e.slice(-4),a="of-type"===t;return 1===r&&0===i?function(e){return!!e.parentNode}:function(t,n,u){var l,c,f,p,h,d,g=o!==s?"nextSibling":"previousSibling",m=t.parentNode,v=a&&t.nodeName.toLowerCase(),x=!u&&!a;if(m){if(o){while(g){f=t;while(f=f[g])if(a?f.nodeName.toLowerCase()===v:1===f.nodeType)return!1;d=g="only"===e&&!d&&"nextSibling"}return!0}if(d=[s?m.firstChild:m.lastChild],s&&x){c=m[y]||(m[y]={}),l=c[e]||[],h=l[0]===w&&l[1],p=l[0]===w&&l[2],f=h&&m.childNodes[h];while(f=++h&&f&&f[g]||(p=h=0)||d.pop())if(1===f.nodeType&&++p&&f===t){c[e]=[w,h,p];break}}else if(x&&(l=(t[y]||(t[y]={}))[e])&&l[0]===w)p=l[1];else while(f=++h&&f&&f[g]||(p=h=0)||d.pop())if((a?f.nodeName.toLowerCase()===v:1===f.nodeType)&&++p&&(x&&((f[y]||(f[y]={}))[e]=[w,p]),f===t))break;return p-=i,p===r||0===p%r&&p/r>=0}}},PSEUDO:function(e,t){var n,i=r.pseudos[e]||r.setFilters[e.toLowerCase()]||ut.error("unsupported pseudo: "+e);return i[y]?i(t):i.length>1?(n=[e,e,"",t],r.setFilters.hasOwnProperty(e.toLowerCase())?st(function(e,n){var r,o=i(e,t),s=o.length;while(s--)r=F.call(e,o[s]),e[r]=!(n[r]=o[s])}):function(e){return i(e,0,n)}):i}},pseudos:{not:st(function(e){var t=[],n=[],r=s(e.replace(I,"$1"));return r[y]?st(function(e,t,n,i){var o,s=r(e,null,i,[]),a=e.length;while(a--)(o=s[a])&&(e[a]=!(t[a]=o))}):function(e,i,o){return t[0]=e,r(t,null,o,n),!n.pop()}}),has:st(function(e){return function(t){return ut(e,t).length>0}}),contains:st(function(e){return function(t){return(t.textContent||t.innerText||i(t)).indexOf(e)>-1}}),lang:st(function(e){return V.test(e||"")||ut.error("unsupported lang: "+e),e=e.replace(tt,nt).toLowerCase(),function(t){var n;do if(n=p?t.lang:t.getAttribute("xml:lang")||t.getAttribute("lang"))return n=n.toLowerCase(),n===e||0===n.indexOf(e+"-");while((t=t.parentNode)&&1===t.nodeType);return!1}}),target:function(t){var n=e.location&&e.location.hash;return n&&n.slice(1)===t.id},root:function(e){return e===f},focus:function(e){return e===c.activeElement&&(!c.hasFocus||c.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:function(e){return e.disabled===!1},disabled:function(e){return e.disabled===!0},checked:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&!!e.checked||"option"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,e.selected===!0},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeName>"@"||3===e.nodeType||4===e.nodeType)return!1;return!0},parent:function(e){return!r.pseudos.empty(e)},header:function(e){return Z.test(e.nodeName)},input:function(e){return K.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&"button"===e.type||"button"===t},text:function(e){var t;return"input"===e.nodeName.toLowerCase()&&"text"===e.type&&(null==(t=e.getAttribute("type"))||t.toLowerCase()===e.type)},first:dt(function(){return[0]}),last:dt(function(e,t){return[t-1]}),eq:dt(function(e,t,n){return[0>n?n+t:n]}),even:dt(function(e,t){var n=0;for(;t>n;n+=2)e.push(n);return e}),odd:dt(function(e,t){var n=1;for(;t>n;n+=2)e.push(n);return e}),lt:dt(function(e,t,n){var r=0>n?n+t:n;for(;--r>=0;)e.push(r);return e}),gt:dt(function(e,t,n){var r=0>n?n+t:n;for(;t>++r;)e.push(r);return e})}};for(t in{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})r.pseudos[t]=pt(t);for(t in{submit:!0,reset:!0})r.pseudos[t]=ht(t);function gt(e,t){var n,i,o,s,a,u,l,c=k[e+" "];if(c)return t?0:c.slice(0);a=e,u=[],l=r.preFilter;while(a){(!n||(i=z.exec(a)))&&(i&&(a=a.slice(i[0].length)||a),u.push(o=[])),n=!1,(i=_.exec(a))&&(n=i.shift(),o.push({value:n,type:i[0].replace(I," ")}),a=a.slice(n.length));for(s in r.filter)!(i=G[s].exec(a))||l[s]&&!(i=l[s](i))||(n=i.shift(),o.push({value:n,type:s,matches:i}),a=a.slice(n.length));if(!n)break}return t?a.length:a?ut.error(e):k(e,u).slice(0)}function mt(e){var t=0,n=e.length,r="";for(;n>t;t++)r+=e[t].value;return r}function yt(e,t,r){var i=t.dir,o=r&&"parentNode"===i,s=T++;return t.first?function(t,n,r){while(t=t[i])if(1===t.nodeType||o)return e(t,n,r)}:function(t,r,a){var u,l,c,f=w+" "+s;if(a){while(t=t[i])if((1===t.nodeType||o)&&e(t,r,a))return!0}else while(t=t[i])if(1===t.nodeType||o)if(c=t[y]||(t[y]={}),(l=c[i])&&l[0]===f){if((u=l[1])===!0||u===n)return u===!0}else if(l=c[i]=[f],l[1]=e(t,r,a)||n,l[1]===!0)return!0}}function vt(e){return e.length>1?function(t,n,r){var i=e.length;while(i--)if(!e[i](t,n,r))return!1;return!0}:e[0]}function xt(e,t,n,r,i){var o,s=[],a=0,u=e.length,l=null!=t;for(;u>a;a++)(o=e[a])&&(!n||n(o,r,i))&&(s.push(o),l&&t.push(a));return s}function bt(e,t,n,r,i,o){return r&&!r[y]&&(r=bt(r)),i&&!i[y]&&(i=bt(i,o)),st(function(o,s,a,u){var l,c,f,p=[],h=[],d=s.length,g=o||Ct(t||"*",a.nodeType?[a]:a,[]),m=!e||!o&&t?g:xt(g,p,e,a,u),y=n?i||(o?e:d||r)?[]:s:m;if(n&&n(m,y,a,u),r){l=xt(y,h),r(l,[],a,u),c=l.length;while(c--)(f=l[c])&&(y[h[c]]=!(m[h[c]]=f))}if(o){if(i||e){if(i){l=[],c=y.length;while(c--)(f=y[c])&&l.push(m[c]=f);i(null,y=[],l,u)}c=y.length;while(c--)(f=y[c])&&(l=i?F.call(o,f):p[c])>-1&&(o[l]=!(s[l]=f))}}else y=xt(y===s?y.splice(d,y.length):y),i?i(null,s,y,u):H.apply(s,y)})}function wt(e){var t,n,i,o=e.length,s=r.relative[e[0].type],u=s||r.relative[" "],l=s?1:0,c=yt(function(e){return e===t},u,!0),f=yt(function(e){return F.call(t,e)>-1},u,!0),p=[function(e,n,r){return!s&&(r||n!==a)||((t=n).nodeType?c(e,n,r):f(e,n,r))}];for(;o>l;l++)if(n=r.relative[e[l].type])p=[yt(vt(p),n)];else{if(n=r.filter[e[l].type].apply(null,e[l].matches),n[y]){for(i=++l;o>i;i++)if(r.relative[e[i].type])break;return bt(l>1&&vt(p),l>1&&mt(e.slice(0,l-1)).replace(I,"$1"),n,i>l&&wt(e.slice(l,i)),o>i&&wt(e=e.slice(i)),o>i&&mt(e))}p.push(n)}return vt(p)}function Tt(e,t){var i=0,o=t.length>0,s=e.length>0,u=function(u,l,f,p,h){var d,g,m,y=[],v=0,x="0",b=u&&[],T=null!=h,C=a,k=u||s&&r.find.TAG("*",h&&l.parentNode||l),N=w+=null==C?1:Math.random()||.1;for(T&&(a=l!==c&&l,n=i);null!=(d=k[x]);x++){if(s&&d){g=0;while(m=e[g++])if(m(d,l,f)){p.push(d);break}T&&(w=N,n=++i)}o&&((d=!m&&d)&&v--,u&&b.push(d))}if(v+=x,o&&x!==v){g=0;while(m=t[g++])m(b,y,l,f);if(u){if(v>0)while(x--)b[x]||y[x]||(y[x]=L.call(p));y=xt(y)}H.apply(p,y),T&&!u&&y.length>0&&v+t.length>1&&ut.uniqueSort(p)}return T&&(w=N,a=C),b};return o?st(u):u}s=ut.compile=function(e,t){var n,r=[],i=[],o=N[e+" "];if(!o){t||(t=gt(e)),n=t.length;while(n--)o=wt(t[n]),o[y]?r.push(o):i.push(o);o=N(e,Tt(i,r))}return o};function Ct(e,t,n){var r=0,i=t.length;for(;i>r;r++)ut(e,t[r],n);return n}function kt(e,t,n,i){var o,a,u,l,c,f=gt(e);if(!i&&1===f.length){if(a=f[0]=f[0].slice(0),a.length>2&&"ID"===(u=a[0]).type&&9===t.nodeType&&p&&r.relative[a[1].type]){if(t=(r.find.ID(u.matches[0].replace(tt,nt),t)||[])[0],!t)return n;e=e.slice(a.shift().value.length)}o=G.needsContext.test(e)?0:a.length;while(o--){if(u=a[o],r.relative[l=u.type])break;if((c=r.find[l])&&(i=c(u.matches[0].replace(tt,nt),X.test(a[0].type)&&t.parentNode||t))){if(a.splice(o,1),e=i.length&&mt(a),!e)return H.apply(n,i),n;break}}}return s(e,f)(i,t,!p,n,X.test(e)),n}r.pseudos.nth=r.pseudos.eq;function Nt(){}Nt.prototype=r.filters=r.pseudos,r.setFilters=new Nt,b.sortStable=y.split("").sort(S).join("")===y,l(),[0,0].sort(S),b.detectDuplicates=E,at(function(e){if(e.innerHTML="<a href='#'></a>","#"!==e.firstChild.getAttribute("href")){var t="type|href|height|width".split("|"),n=t.length;while(n--)r.attrHandle[t[n]]=ft}}),at(function(e){if(null!=e.getAttribute("disabled")){var t=P.split("|"),n=t.length;while(n--)r.attrHandle[t[n]]=ct}}),x.find=ut,x.expr=ut.selectors,x.expr[":"]=x.expr.pseudos,x.unique=ut.uniqueSort,x.text=ut.getText,x.isXMLDoc=ut.isXML,x.contains=ut.contains}(e);var D={};function A(e){var t=D[e]={};return x.each(e.match(w)||[],function(e,n){t[n]=!0}),t}x.Callbacks=function(e){e="string"==typeof e?D[e]||A(e):x.extend({},e);var t,n,r,i,o,s,a=[],u=!e.once&&[],l=function(f){for(t=e.memory&&f,n=!0,s=i||0,i=0,o=a.length,r=!0;a&&o>s;s++)if(a[s].apply(f[0],f[1])===!1&&e.stopOnFalse){t=!1;break}r=!1,a&&(u?u.length&&l(u.shift()):t?a=[]:c.disable())},c={add:function(){if(a){var n=a.length;(function s(t){x.each(t,function(t,n){var r=x.type(n);"function"===r?e.unique&&c.has(n)||a.push(n):n&&n.length&&"string"!==r&&s(n)})})(arguments),r?o=a.length:t&&(i=n,l(t))}return this},remove:function(){return a&&x.each(arguments,function(e,t){var n;while((n=x.inArray(t,a,n))>-1)a.splice(n,1),r&&(o>=n&&o--,s>=n&&s--)}),this},has:function(e){return e?x.inArray(e,a)>-1:!(!a||!a.length)},empty:function(){return a=[],o=0,this},disable:function(){return a=u=t=undefined,this},disabled:function(){return!a},lock:function(){return u=undefined,t||c.disable(),this},locked:function(){return!u},fireWith:function(e,t){return t=t||[],t=[e,t.slice?t.slice():t],!a||n&&!u||(r?u.push(t):l(t)),this},fire:function(){return c.fireWith(this,arguments),this},fired:function(){return!!n}};return c},x.extend({Deferred:function(e){var t=[["resolve","done",x.Callbacks("once memory"),"resolved"],["reject","fail",x.Callbacks("once memory"),"rejected"],["notify","progress",x.Callbacks("memory")]],n="pending",r={state:function(){return n},always:function(){return i.done(arguments).fail(arguments),this},then:function(){var e=arguments;return x.Deferred(function(n){x.each(t,function(t,o){var s=o[0],a=x.isFunction(e[t])&&e[t];i[o[1]](function(){var e=a&&a.apply(this,arguments);e&&x.isFunction(e.promise)?e.promise().done(n.resolve).fail(n.reject).progress(n.notify):n[s+"With"](this===r?n.promise():this,a?[e]:arguments)})}),e=null}).promise()},promise:function(e){return null!=e?x.extend(e,r):r}},i={};return r.pipe=r.then,x.each(t,function(e,o){var s=o[2],a=o[3];r[o[1]]=s.add,a&&s.add(function(){n=a},t[1^e][2].disable,t[2][2].lock),i[o[0]]=function(){return i[o[0]+"With"](this===i?r:this,arguments),this},i[o[0]+"With"]=s.fireWith}),r.promise(i),e&&e.call(i,i),i},when:function(e){var t=0,n=d.call(arguments),r=n.length,i=1!==r||e&&x.isFunction(e.promise)?r:0,o=1===i?e:x.Deferred(),s=function(e,t,n){return function(r){t[e]=this,n[e]=arguments.length>1?d.call(arguments):r,n===a?o.notifyWith(t,n):--i||o.resolveWith(t,n)}},a,u,l;if(r>1)for(a=Array(r),u=Array(r),l=Array(r);r>t;t++)n[t]&&x.isFunction(n[t].promise)?n[t].promise().done(s(t,l,n)).fail(o.reject).progress(s(t,u,a)):--i;return i||o.resolveWith(l,n),o.promise()}}),x.support=function(t){var n=o.createElement("input"),r=o.createDocumentFragment(),i=o.createElement("div"),s=o.createElement("select"),a=s.appendChild(o.createElement("option"));return n.type?(n.type="checkbox",t.checkOn=""!==n.value,t.optSelected=a.selected,t.reliableMarginRight=!0,t.boxSizingReliable=!0,t.pixelPosition=!1,n.checked=!0,t.noCloneChecked=n.cloneNode(!0).checked,s.disabled=!0,t.optDisabled=!a.disabled,n=o.createElement("input"),n.value="t",n.type="radio",t.radioValue="t"===n.value,n.setAttribute("checked","t"),n.setAttribute("name","t"),r.appendChild(n),t.checkClone=r.cloneNode(!0).cloneNode(!0).lastChild.checked,t.focusinBubbles="onfocusin"in e,i.style.backgroundClip="content-box",i.cloneNode(!0).style.backgroundClip="",t.clearCloneStyle="content-box"===i.style.backgroundClip,x(function(){var n,r,s="padding:0;margin:0;border:0;display:block;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box",a=o.getElementsByTagName("body")[0];a&&(n=o.createElement("div"),n.style.cssText="border:0;width:0;height:0;position:absolute;top:0;left:-9999px;margin-top:1px",a.appendChild(n).appendChild(i),i.innerHTML="",i.style.cssText="-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:1px;border:1px;display:block;width:4px;margin-top:1%;position:absolute;top:1%",x.swap(a,null!=a.style.zoom?{zoom:1}:{},function(){t.boxSizing=4===i.offsetWidth}),e.getComputedStyle&&(t.pixelPosition="1%"!==(e.getComputedStyle(i,null)||{}).top,t.boxSizingReliable="4px"===(e.getComputedStyle(i,null)||{width:"4px"}).width,r=i.appendChild(o.createElement("div")),r.style.cssText=i.style.cssText=s,r.style.marginRight=r.style.width="0",i.style.width="1px",t.reliableMarginRight=!parseFloat((e.getComputedStyle(r,null)||{}).marginRight)),a.removeChild(n))}),t):t}({});var L,q,H=/(?:\{[\s\S]*\}|\[[\s\S]*\])$/,O=/([A-Z])/g;function F(){Object.defineProperty(this.cache={},0,{get:function(){return{}}}),this.expando=x.expando+Math.random()}F.uid=1,F.accepts=function(e){return e.nodeType?1===e.nodeType||9===e.nodeType:!0},F.prototype={key:function(e){if(!F.accepts(e))return 0;var t={},n=e[this.expando];if(!n){n=F.uid++;try{t[this.expando]={value:n},Object.defineProperties(e,t)}catch(r){t[this.expando]=n,x.extend(e,t)}}return this.cache[n]||(this.cache[n]={}),n},set:function(e,t,n){var r,i=this.key(e),o=this.cache[i];if("string"==typeof t)o[t]=n;else if(x.isEmptyObject(o))this.cache[i]=t;else for(r in t)o[r]=t[r]},get:function(e,t){var n=this.cache[this.key(e)];return t===undefined?n:n[t]},access:function(e,t,n){return t===undefined||t&&"string"==typeof t&&n===undefined?this.get(e,t):(this.set(e,t,n),n!==undefined?n:t)},remove:function(e,t){var n,r,i=this.key(e),o=this.cache[i];if(t===undefined)this.cache[i]={};else{x.isArray(t)?r=t.concat(t.map(x.camelCase)):t in o?r=[t]:(r=x.camelCase(t),r=r in o?[r]:r.match(w)||[]),n=r.length;while(n--)delete o[r[n]]}},hasData:function(e){return!x.isEmptyObject(this.cache[e[this.expando]]||{})},discard:function(e){delete this.cache[this.key(e)]}},L=new F,q=new F,x.extend({acceptData:F.accepts,hasData:function(e){return L.hasData(e)||q.hasData(e)},data:function(e,t,n){return L.access(e,t,n)},removeData:function(e,t){L.remove(e,t)},_data:function(e,t,n){return q.access(e,t,n)},_removeData:function(e,t){q.remove(e,t)}}),x.fn.extend({data:function(e,t){var n,r,i=this[0],o=0,s=null;if(e===undefined){if(this.length&&(s=L.get(i),1===i.nodeType&&!q.get(i,"hasDataAttrs"))){for(n=i.attributes;n.length>o;o++)r=n[o].name,0===r.indexOf("data-")&&(r=x.camelCase(r.substring(5)),P(i,r,s[r]));q.set(i,"hasDataAttrs",!0)}return s}return"object"==typeof e?this.each(function(){L.set(this,e)}):x.access(this,function(t){var n,r=x.camelCase(e);if(i&&t===undefined){if(n=L.get(i,e),n!==undefined)return n;if(n=L.get(i,r),n!==undefined)return n;if(n=P(i,r,undefined),n!==undefined)return n}else this.each(function(){var n=L.get(this,r);L.set(this,r,t),-1!==e.indexOf("-")&&n!==undefined&&L.set(this,e,t)})},null,t,arguments.length>1,null,!0)},removeData:function(e){return this.each(function(){L.remove(this,e)})}});function P(e,t,n){var r;if(n===undefined&&1===e.nodeType)if(r="data-"+t.replace(O,"-$1").toLowerCase(),n=e.getAttribute(r),"string"==typeof n){try{n="true"===n?!0:"false"===n?!1:"null"===n?null:+n+""===n?+n:H.test(n)?JSON.parse(n):n}catch(i){}L.set(e,t,n)}else n=undefined;return n}x.extend({queue:function(e,t,n){var r;return e?(t=(t||"fx")+"queue",r=q.get(e,t),n&&(!r||x.isArray(n)?r=q.access(e,t,x.makeArray(n)):r.push(n)),r||[]):undefined},dequeue:function(e,t){t=t||"fx";var n=x.queue(e,t),r=n.length,i=n.shift(),o=x._queueHooks(e,t),s=function(){x.dequeue(e,t)};"inprogress"===i&&(i=n.shift(),r--),o.cur=i,i&&("fx"===t&&n.unshift("inprogress"),delete o.stop,i.call(e,s,o)),!r&&o&&o.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return q.get(e,n)||q.access(e,n,{empty:x.Callbacks("once memory").add(function(){q.remove(e,[t+"queue",n])})})}}),x.fn.extend({queue:function(e,t){var n=2;return"string"!=typeof e&&(t=e,e="fx",n--),n>arguments.length?x.queue(this[0],e):t===undefined?this:this.each(function(){var n=x.queue(this,e,t);
+x._queueHooks(this,e),"fx"===e&&"inprogress"!==n[0]&&x.dequeue(this,e)})},dequeue:function(e){return this.each(function(){x.dequeue(this,e)})},delay:function(e,t){return e=x.fx?x.fx.speeds[e]||e:e,t=t||"fx",this.queue(t,function(t,n){var r=setTimeout(t,e);n.stop=function(){clearTimeout(r)}})},clearQueue:function(e){return this.queue(e||"fx",[])},promise:function(e,t){var n,r=1,i=x.Deferred(),o=this,s=this.length,a=function(){--r||i.resolveWith(o,[o])};"string"!=typeof e&&(t=e,e=undefined),e=e||"fx";while(s--)n=q.get(o[s],e+"queueHooks"),n&&n.empty&&(r++,n.empty.add(a));return a(),i.promise(t)}});var R,M,W=/[\t\r\n]/g,$=/\r/g,B=/^(?:input|select|textarea|button)$/i;x.fn.extend({attr:function(e,t){return x.access(this,x.attr,e,t,arguments.length>1)},removeAttr:function(e){return this.each(function(){x.removeAttr(this,e)})},prop:function(e,t){return x.access(this,x.prop,e,t,arguments.length>1)},removeProp:function(e){return this.each(function(){delete this[x.propFix[e]||e]})},addClass:function(e){var t,n,r,i,o,s=0,a=this.length,u="string"==typeof e&&e;if(x.isFunction(e))return this.each(function(t){x(this).addClass(e.call(this,t,this.className))});if(u)for(t=(e||"").match(w)||[];a>s;s++)if(n=this[s],r=1===n.nodeType&&(n.className?(" "+n.className+" ").replace(W," "):" ")){o=0;while(i=t[o++])0>r.indexOf(" "+i+" ")&&(r+=i+" ");n.className=x.trim(r)}return this},removeClass:function(e){var t,n,r,i,o,s=0,a=this.length,u=0===arguments.length||"string"==typeof e&&e;if(x.isFunction(e))return this.each(function(t){x(this).removeClass(e.call(this,t,this.className))});if(u)for(t=(e||"").match(w)||[];a>s;s++)if(n=this[s],r=1===n.nodeType&&(n.className?(" "+n.className+" ").replace(W," "):"")){o=0;while(i=t[o++])while(r.indexOf(" "+i+" ")>=0)r=r.replace(" "+i+" "," ");n.className=e?x.trim(r):""}return this},toggleClass:function(e,t){var n=typeof e,i="boolean"==typeof t;return x.isFunction(e)?this.each(function(n){x(this).toggleClass(e.call(this,n,this.className,t),t)}):this.each(function(){if("string"===n){var o,s=0,a=x(this),u=t,l=e.match(w)||[];while(o=l[s++])u=i?u:!a.hasClass(o),a[u?"addClass":"removeClass"](o)}else(n===r||"boolean"===n)&&(this.className&&q.set(this,"__className__",this.className),this.className=this.className||e===!1?"":q.get(this,"__className__")||"")})},hasClass:function(e){var t=" "+e+" ",n=0,r=this.length;for(;r>n;n++)if(1===this[n].nodeType&&(" "+this[n].className+" ").replace(W," ").indexOf(t)>=0)return!0;return!1},val:function(e){var t,n,r,i=this[0];{if(arguments.length)return r=x.isFunction(e),this.each(function(n){var i,o=x(this);1===this.nodeType&&(i=r?e.call(this,n,o.val()):e,null==i?i="":"number"==typeof i?i+="":x.isArray(i)&&(i=x.map(i,function(e){return null==e?"":e+""})),t=x.valHooks[this.type]||x.valHooks[this.nodeName.toLowerCase()],t&&"set"in t&&t.set(this,i,"value")!==undefined||(this.value=i))});if(i)return t=x.valHooks[i.type]||x.valHooks[i.nodeName.toLowerCase()],t&&"get"in t&&(n=t.get(i,"value"))!==undefined?n:(n=i.value,"string"==typeof n?n.replace($,""):null==n?"":n)}}}),x.extend({valHooks:{option:{get:function(e){var t=e.attributes.value;return!t||t.specified?e.value:e.text}},select:{get:function(e){var t,n,r=e.options,i=e.selectedIndex,o="select-one"===e.type||0>i,s=o?null:[],a=o?i+1:r.length,u=0>i?a:o?i:0;for(;a>u;u++)if(n=r[u],!(!n.selected&&u!==i||(x.support.optDisabled?n.disabled:null!==n.getAttribute("disabled"))||n.parentNode.disabled&&x.nodeName(n.parentNode,"optgroup"))){if(t=x(n).val(),o)return t;s.push(t)}return s},set:function(e,t){var n,r,i=e.options,o=x.makeArray(t),s=i.length;while(s--)r=i[s],(r.selected=x.inArray(x(r).val(),o)>=0)&&(n=!0);return n||(e.selectedIndex=-1),o}}},attr:function(e,t,n){var i,o,s=e.nodeType;if(e&&3!==s&&8!==s&&2!==s)return typeof e.getAttribute===r?x.prop(e,t,n):(1===s&&x.isXMLDoc(e)||(t=t.toLowerCase(),i=x.attrHooks[t]||(x.expr.match.boolean.test(t)?M:R)),n===undefined?i&&"get"in i&&null!==(o=i.get(e,t))?o:(o=x.find.attr(e,t),null==o?undefined:o):null!==n?i&&"set"in i&&(o=i.set(e,n,t))!==undefined?o:(e.setAttribute(t,n+""),n):(x.removeAttr(e,t),undefined))},removeAttr:function(e,t){var n,r,i=0,o=t&&t.match(w);if(o&&1===e.nodeType)while(n=o[i++])r=x.propFix[n]||n,x.expr.match.boolean.test(n)&&(e[r]=!1),e.removeAttribute(n)},attrHooks:{type:{set:function(e,t){if(!x.support.radioValue&&"radio"===t&&x.nodeName(e,"input")){var n=e.value;return e.setAttribute("type",t),n&&(e.value=n),t}}}},propFix:{"for":"htmlFor","class":"className"},prop:function(e,t,n){var r,i,o,s=e.nodeType;if(e&&3!==s&&8!==s&&2!==s)return o=1!==s||!x.isXMLDoc(e),o&&(t=x.propFix[t]||t,i=x.propHooks[t]),n!==undefined?i&&"set"in i&&(r=i.set(e,n,t))!==undefined?r:e[t]=n:i&&"get"in i&&null!==(r=i.get(e,t))?r:e[t]},propHooks:{tabIndex:{get:function(e){return e.hasAttribute("tabindex")||B.test(e.nodeName)||e.href?e.tabIndex:-1}}}}),M={set:function(e,t,n){return t===!1?x.removeAttr(e,n):e.setAttribute(n,n),n}},x.each(x.expr.match.boolean.source.match(/\w+/g),function(e,t){var n=x.expr.attrHandle[t]||x.find.attr;x.expr.attrHandle[t]=function(e,t,r){var i=x.expr.attrHandle[t],o=r?undefined:(x.expr.attrHandle[t]=undefined)!=n(e,t,r)?t.toLowerCase():null;return x.expr.attrHandle[t]=i,o}}),x.support.optSelected||(x.propHooks.selected={get:function(e){var t=e.parentNode;return t&&t.parentNode&&t.parentNode.selectedIndex,null}}),x.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){x.propFix[this.toLowerCase()]=this}),x.each(["radio","checkbox"],function(){x.valHooks[this]={set:function(e,t){return x.isArray(t)?e.checked=x.inArray(x(e).val(),t)>=0:undefined}},x.support.checkOn||(x.valHooks[this].get=function(e){return null===e.getAttribute("value")?"on":e.value})});var I=/^key/,z=/^(?:mouse|contextmenu)|click/,_=/^(?:focusinfocus|focusoutblur)$/,X=/^([^.]*)(?:\.(.+)|)$/;function U(){return!0}function Y(){return!1}function V(){try{return o.activeElement}catch(e){}}x.event={global:{},add:function(e,t,n,i,o){var s,a,u,l,c,f,p,h,d,g,m,y=q.get(e);if(y){n.handler&&(s=n,n=s.handler,o=s.selector),n.guid||(n.guid=x.guid++),(l=y.events)||(l=y.events={}),(a=y.handle)||(a=y.handle=function(e){return typeof x===r||e&&x.event.triggered===e.type?undefined:x.event.dispatch.apply(a.elem,arguments)},a.elem=e),t=(t||"").match(w)||[""],c=t.length;while(c--)u=X.exec(t[c])||[],d=m=u[1],g=(u[2]||"").split(".").sort(),d&&(p=x.event.special[d]||{},d=(o?p.delegateType:p.bindType)||d,p=x.event.special[d]||{},f=x.extend({type:d,origType:m,data:i,handler:n,guid:n.guid,selector:o,needsContext:o&&x.expr.match.needsContext.test(o),namespace:g.join(".")},s),(h=l[d])||(h=l[d]=[],h.delegateCount=0,p.setup&&p.setup.call(e,i,g,a)!==!1||e.addEventListener&&e.addEventListener(d,a,!1)),p.add&&(p.add.call(e,f),f.handler.guid||(f.handler.guid=n.guid)),o?h.splice(h.delegateCount++,0,f):h.push(f),x.event.global[d]=!0);e=null}},remove:function(e,t,n,r,i){var o,s,a,u,l,c,f,p,h,d,g,m=q.hasData(e)&&q.get(e);if(m&&(u=m.events)){t=(t||"").match(w)||[""],l=t.length;while(l--)if(a=X.exec(t[l])||[],h=g=a[1],d=(a[2]||"").split(".").sort(),h){f=x.event.special[h]||{},h=(r?f.delegateType:f.bindType)||h,p=u[h]||[],a=a[2]&&RegExp("(^|\\.)"+d.join("\\.(?:.*\\.|)")+"(\\.|$)"),s=o=p.length;while(o--)c=p[o],!i&&g!==c.origType||n&&n.guid!==c.guid||a&&!a.test(c.namespace)||r&&r!==c.selector&&("**"!==r||!c.selector)||(p.splice(o,1),c.selector&&p.delegateCount--,f.remove&&f.remove.call(e,c));s&&!p.length&&(f.teardown&&f.teardown.call(e,d,m.handle)!==!1||x.removeEvent(e,h,m.handle),delete u[h])}else for(h in u)x.event.remove(e,h+t[l],n,r,!0);x.isEmptyObject(u)&&(delete m.handle,q.remove(e,"events"))}},trigger:function(t,n,r,i){var s,a,u,l,c,f,p,h=[r||o],d=y.call(t,"type")?t.type:t,g=y.call(t,"namespace")?t.namespace.split("."):[];if(a=u=r=r||o,3!==r.nodeType&&8!==r.nodeType&&!_.test(d+x.event.triggered)&&(d.indexOf(".")>=0&&(g=d.split("."),d=g.shift(),g.sort()),c=0>d.indexOf(":")&&"on"+d,t=t[x.expando]?t:new x.Event(d,"object"==typeof t&&t),t.isTrigger=i?2:3,t.namespace=g.join("."),t.namespace_re=t.namespace?RegExp("(^|\\.)"+g.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,t.result=undefined,t.target||(t.target=r),n=null==n?[t]:x.makeArray(n,[t]),p=x.event.special[d]||{},i||!p.trigger||p.trigger.apply(r,n)!==!1)){if(!i&&!p.noBubble&&!x.isWindow(r)){for(l=p.delegateType||d,_.test(l+d)||(a=a.parentNode);a;a=a.parentNode)h.push(a),u=a;u===(r.ownerDocument||o)&&h.push(u.defaultView||u.parentWindow||e)}s=0;while((a=h[s++])&&!t.isPropagationStopped())t.type=s>1?l:p.bindType||d,f=(q.get(a,"events")||{})[t.type]&&q.get(a,"handle"),f&&f.apply(a,n),f=c&&a[c],f&&x.acceptData(a)&&f.apply&&f.apply(a,n)===!1&&t.preventDefault();return t.type=d,i||t.isDefaultPrevented()||p._default&&p._default.apply(h.pop(),n)!==!1||!x.acceptData(r)||c&&x.isFunction(r[d])&&!x.isWindow(r)&&(u=r[c],u&&(r[c]=null),x.event.triggered=d,r[d](),x.event.triggered=undefined,u&&(r[c]=u)),t.result}},dispatch:function(e){e=x.event.fix(e);var t,n,r,i,o,s=[],a=d.call(arguments),u=(q.get(this,"events")||{})[e.type]||[],l=x.event.special[e.type]||{};if(a[0]=e,e.delegateTarget=this,!l.preDispatch||l.preDispatch.call(this,e)!==!1){s=x.event.handlers.call(this,e,u),t=0;while((i=s[t++])&&!e.isPropagationStopped()){e.currentTarget=i.elem,n=0;while((o=i.handlers[n++])&&!e.isImmediatePropagationStopped())(!e.namespace_re||e.namespace_re.test(o.namespace))&&(e.handleObj=o,e.data=o.data,r=((x.event.special[o.origType]||{}).handle||o.handler).apply(i.elem,a),r!==undefined&&(e.result=r)===!1&&(e.preventDefault(),e.stopPropagation()))}return l.postDispatch&&l.postDispatch.call(this,e),e.result}},handlers:function(e,t){var n,r,i,o,s=[],a=t.delegateCount,u=e.target;if(a&&u.nodeType&&(!e.button||"click"!==e.type))for(;u!==this;u=u.parentNode||this)if(u.disabled!==!0||"click"!==e.type){for(r=[],n=0;a>n;n++)o=t[n],i=o.selector+" ",r[i]===undefined&&(r[i]=o.needsContext?x(i,this).index(u)>=0:x.find(i,this,null,[u]).length),r[i]&&r.push(o);r.length&&s.push({elem:u,handlers:r})}return t.length>a&&s.push({elem:this,handlers:t.slice(a)}),s},props:"altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),fixHooks:{},keyHooks:{props:"char charCode key keyCode".split(" "),filter:function(e,t){return null==e.which&&(e.which=null!=t.charCode?t.charCode:t.keyCode),e}},mouseHooks:{props:"button buttons clientX clientY offsetX offsetY pageX pageY screenX screenY toElement".split(" "),filter:function(e,t){var n,r,i,s=t.button;return null==e.pageX&&null!=t.clientX&&(n=e.target.ownerDocument||o,r=n.documentElement,i=n.body,e.pageX=t.clientX+(r&&r.scrollLeft||i&&i.scrollLeft||0)-(r&&r.clientLeft||i&&i.clientLeft||0),e.pageY=t.clientY+(r&&r.scrollTop||i&&i.scrollTop||0)-(r&&r.clientTop||i&&i.clientTop||0)),e.which||s===undefined||(e.which=1&s?1:2&s?3:4&s?2:0),e}},fix:function(e){if(e[x.expando])return e;var t,n,r,i=e.type,o=e,s=this.fixHooks[i];s||(this.fixHooks[i]=s=z.test(i)?this.mouseHooks:I.test(i)?this.keyHooks:{}),r=s.props?this.props.concat(s.props):this.props,e=new x.Event(o),t=r.length;while(t--)n=r[t],e[n]=o[n];return 3===e.target.nodeType&&(e.target=e.target.parentNode),s.filter?s.filter(e,o):e},special:{load:{noBubble:!0},focus:{trigger:function(){return this!==V()&&this.focus?(this.focus(),!1):undefined},delegateType:"focusin"},blur:{trigger:function(){return this===V()&&this.blur?(this.blur(),!1):undefined},delegateType:"focusout"},click:{trigger:function(){return"checkbox"===this.type&&this.click&&x.nodeName(this,"input")?(this.click(),!1):undefined},_default:function(e){return x.nodeName(e.target,"a")}},beforeunload:{postDispatch:function(e){e.result!==undefined&&(e.originalEvent.returnValue=e.result)}}},simulate:function(e,t,n,r){var i=x.extend(new x.Event,n,{type:e,isSimulated:!0,originalEvent:{}});r?x.event.trigger(i,null,t):x.event.dispatch.call(t,i),i.isDefaultPrevented()&&n.preventDefault()}},x.removeEvent=function(e,t,n){e.removeEventListener&&e.removeEventListener(t,n,!1)},x.Event=function(e,t){return this instanceof x.Event?(e&&e.type?(this.originalEvent=e,this.type=e.type,this.isDefaultPrevented=e.defaultPrevented||e.getPreventDefault&&e.getPreventDefault()?U:Y):this.type=e,t&&x.extend(this,t),this.timeStamp=e&&e.timeStamp||x.now(),this[x.expando]=!0,undefined):new x.Event(e,t)},x.Event.prototype={isDefaultPrevented:Y,isPropagationStopped:Y,isImmediatePropagationStopped:Y,preventDefault:function(){var e=this.originalEvent;this.isDefaultPrevented=U,e&&e.preventDefault&&e.preventDefault()},stopPropagation:function(){var e=this.originalEvent;this.isPropagationStopped=U,e&&e.stopPropagation&&e.stopPropagation()},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=U,this.stopPropagation()}},x.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(e,t){x.event.special[e]={delegateType:t,bindType:t,handle:function(e){var n,r=this,i=e.relatedTarget,o=e.handleObj;return(!i||i!==r&&!x.contains(r,i))&&(e.type=o.origType,n=o.handler.apply(this,arguments),e.type=t),n}}}),x.support.focusinBubbles||x.each({focus:"focusin",blur:"focusout"},function(e,t){var n=0,r=function(e){x.event.simulate(t,e.target,x.event.fix(e),!0)};x.event.special[t]={setup:function(){0===n++&&o.addEventListener(e,r,!0)},teardown:function(){0===--n&&o.removeEventListener(e,r,!0)}}}),x.fn.extend({on:function(e,t,n,r,i){var o,s;if("object"==typeof e){"string"!=typeof t&&(n=n||t,t=undefined);for(s in e)this.on(s,t,n,e[s],i);return this}if(null==n&&null==r?(r=t,n=t=undefined):null==r&&("string"==typeof t?(r=n,n=undefined):(r=n,n=t,t=undefined)),r===!1)r=Y;else if(!r)return this;return 1===i&&(o=r,r=function(e){return x().off(e),o.apply(this,arguments)},r.guid=o.guid||(o.guid=x.guid++)),this.each(function(){x.event.add(this,e,r,n,t)})},one:function(e,t,n,r){return this.on(e,t,n,r,1)},off:function(e,t,n){var r,i;if(e&&e.preventDefault&&e.handleObj)return r=e.handleObj,x(e.delegateTarget).off(r.namespace?r.origType+"."+r.namespace:r.origType,r.selector,r.handler),this;if("object"==typeof e){for(i in e)this.off(i,t,e[i]);return this}return(t===!1||"function"==typeof t)&&(n=t,t=undefined),n===!1&&(n=Y),this.each(function(){x.event.remove(this,e,n,t)})},trigger:function(e,t){return this.each(function(){x.event.trigger(e,t,this)})},triggerHandler:function(e,t){var n=this[0];return n?x.event.trigger(e,t,n,!0):undefined}});var G=/^.[^:#\[\.,]*$/,J=x.expr.match.needsContext,Q={children:!0,contents:!0,next:!0,prev:!0};x.fn.extend({find:function(e){var t,n,r,i=this.length;if("string"!=typeof e)return t=this,this.pushStack(x(e).filter(function(){for(r=0;i>r;r++)if(x.contains(t[r],this))return!0}));for(n=[],r=0;i>r;r++)x.find(e,this[r],n);return n=this.pushStack(i>1?x.unique(n):n),n.selector=(this.selector?this.selector+" ":"")+e,n},has:function(e){var t=x(e,this),n=t.length;return this.filter(function(){var e=0;for(;n>e;e++)if(x.contains(this,t[e]))return!0})},not:function(e){return this.pushStack(Z(this,e||[],!0))},filter:function(e){return this.pushStack(Z(this,e||[],!1))},is:function(e){return!!e&&("string"==typeof e?J.test(e)?x(e,this.context).index(this[0])>=0:x.filter(e,this).length>0:this.filter(e).length>0)},closest:function(e,t){var n,r=0,i=this.length,o=[],s=J.test(e)||"string"!=typeof e?x(e,t||this.context):0;for(;i>r;r++)for(n=this[r];n&&n!==t;n=n.parentNode)if(11>n.nodeType&&(s?s.index(n)>-1:1===n.nodeType&&x.find.matchesSelector(n,e))){n=o.push(n);break}return this.pushStack(o.length>1?x.unique(o):o)},index:function(e){return e?"string"==typeof e?g.call(x(e),this[0]):g.call(this,e.jquery?e[0]:e):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){var n="string"==typeof e?x(e,t):x.makeArray(e&&e.nodeType?[e]:e),r=x.merge(this.get(),n);return this.pushStack(x.unique(r))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}});function K(e,t){while((e=e[t])&&1!==e.nodeType);return e}x.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return x.dir(e,"parentNode")},parentsUntil:function(e,t,n){return x.dir(e,"parentNode",n)},next:function(e){return K(e,"nextSibling")},prev:function(e){return K(e,"previousSibling")},nextAll:function(e){return x.dir(e,"nextSibling")},prevAll:function(e){return x.dir(e,"previousSibling")},nextUntil:function(e,t,n){return x.dir(e,"nextSibling",n)},prevUntil:function(e,t,n){return x.dir(e,"previousSibling",n)},siblings:function(e){return x.sibling((e.parentNode||{}).firstChild,e)},children:function(e){return x.sibling(e.firstChild)},contents:function(e){return x.nodeName(e,"iframe")?e.contentDocument||e.contentWindow.document:x.merge([],e.childNodes)}},function(e,t){x.fn[e]=function(n,r){var i=x.map(this,t,n);return"Until"!==e.slice(-5)&&(r=n),r&&"string"==typeof r&&(i=x.filter(r,i)),this.length>1&&(Q[e]||x.unique(i),"p"===e[0]&&i.reverse()),this.pushStack(i)}}),x.extend({filter:function(e,t,n){var r=t[0];return n&&(e=":not("+e+")"),1===t.length&&1===r.nodeType?x.find.matchesSelector(r,e)?[r]:[]:x.find.matches(e,x.grep(t,function(e){return 1===e.nodeType}))},dir:function(e,t,n){var r=[],i=n!==undefined;while((e=e[t])&&9!==e.nodeType)if(1===e.nodeType){if(i&&x(e).is(n))break;r.push(e)}return r},sibling:function(e,t){var n=[];for(;e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n}});function Z(e,t,n){if(x.isFunction(t))return x.grep(e,function(e,r){return!!t.call(e,r,e)!==n});if(t.nodeType)return x.grep(e,function(e){return e===t!==n});if("string"==typeof t){if(G.test(t))return x.filter(t,e,n);t=x.filter(t,e)}return x.grep(e,function(e){return g.call(t,e)>=0!==n})}var et=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,tt=/<([\w:]+)/,nt=/<|&#?\w+;/,rt=/<(?:script|style|link)/i,it=/^(?:checkbox|radio)$/i,ot=/checked\s*(?:[^=]|=\s*.checked.)/i,st=/^$|\/(?:java|ecma)script/i,at=/^true\/(.*)/,ut=/^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\s*$/g,lt={option:[1,"<select multiple='multiple'>","</select>"],thead:[1,"<table>","</table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],_default:[0,"",""]};lt.optgroup=lt.option,lt.tbody=lt.tfoot=lt.colgroup=lt.caption=lt.col=lt.thead,lt.th=lt.td,x.fn.extend({text:function(e){return x.access(this,function(e){return e===undefined?x.text(this):this.empty().append((this[0]&&this[0].ownerDocument||o).createTextNode(e))},null,e,arguments.length)},append:function(){return this.domManip(arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=ct(this,e);t.appendChild(e)}})},prepend:function(){return this.domManip(arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=ct(this,e);t.insertBefore(e,t.firstChild)}})},before:function(){return this.domManip(arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this)})},after:function(){return this.domManip(arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)})},remove:function(e,t){var n,r=e?x.filter(e,this):this,i=0;for(;null!=(n=r[i]);i++)t||1!==n.nodeType||x.cleanData(gt(n)),n.parentNode&&(t&&x.contains(n.ownerDocument,n)&&ht(gt(n,"script")),n.parentNode.removeChild(n));return this},empty:function(){var e,t=0;for(;null!=(e=this[t]);t++)1===e.nodeType&&(x.cleanData(gt(e,!1)),e.textContent="");return this},clone:function(e,t){return e=null==e?!1:e,t=null==t?e:t,this.map(function(){return x.clone(this,e,t)})},html:function(e){return x.access(this,function(e){var t=this[0]||{},n=0,r=this.length;if(e===undefined&&1===t.nodeType)return t.innerHTML;if("string"==typeof e&&!rt.test(e)&&!lt[(tt.exec(e)||["",""])[1].toLowerCase()]){e=e.replace(et,"<$1></$2>");try{for(;r>n;n++)t=this[n]||{},1===t.nodeType&&(x.cleanData(gt(t,!1)),t.innerHTML=e);t=0}catch(i){}}t&&this.empty().append(e)},null,e,arguments.length)},replaceWith:function(){var e=x.map(this,function(e){return[e.nextSibling,e.parentNode]}),t=0;return this.domManip(arguments,function(n){var r=e[t++],i=e[t++];i&&(x(this).remove(),i.insertBefore(n,r))},!0),t?this:this.remove()},detach:function(e){return this.remove(e,!0)},domManip:function(e,t,n){e=p.apply([],e);var r,i,o,s,a,u,l=0,c=this.length,f=this,h=c-1,d=e[0],g=x.isFunction(d);if(g||!(1>=c||"string"!=typeof d||x.support.checkClone)&&ot.test(d))return this.each(function(r){var i=f.eq(r);g&&(e[0]=d.call(this,r,i.html())),i.domManip(e,t,n)});if(c&&(r=x.buildFragment(e,this[0].ownerDocument,!1,!n&&this),i=r.firstChild,1===r.childNodes.length&&(r=i),i)){for(o=x.map(gt(r,"script"),ft),s=o.length;c>l;l++)a=r,l!==h&&(a=x.clone(a,!0,!0),s&&x.merge(o,gt(a,"script"))),t.call(this[l],a,l);if(s)for(u=o[o.length-1].ownerDocument,x.map(o,pt),l=0;s>l;l++)a=o[l],st.test(a.type||"")&&!q.access(a,"globalEval")&&x.contains(u,a)&&(a.src?x._evalUrl(a.src):x.globalEval(a.textContent.replace(ut,"")))}return this}}),x.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(e,t){x.fn[e]=function(e){var n,r=[],i=x(e),o=i.length-1,s=0;for(;o>=s;s++)n=s===o?this:this.clone(!0),x(i[s])[t](n),h.apply(r,n.get());return this.pushStack(r)}}),x.extend({clone:function(e,t,n){var r,i,o,s,a=e.cloneNode(!0),u=x.contains(e.ownerDocument,e);if(!(x.support.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||x.isXMLDoc(e)))for(s=gt(a),o=gt(e),r=0,i=o.length;i>r;r++)mt(o[r],s[r]);if(t)if(n)for(o=o||gt(e),s=s||gt(a),r=0,i=o.length;i>r;r++)dt(o[r],s[r]);else dt(e,a);return s=gt(a,"script"),s.length>0&&ht(s,!u&>(e,"script")),a},buildFragment:function(e,t,n,r){var i,o,s,a,u,l,c=0,f=e.length,p=t.createDocumentFragment(),h=[];for(;f>c;c++)if(i=e[c],i||0===i)if("object"===x.type(i))x.merge(h,i.nodeType?[i]:i);else if(nt.test(i)){o=o||p.appendChild(t.createElement("div")),s=(tt.exec(i)||["",""])[1].toLowerCase(),a=lt[s]||lt._default,o.innerHTML=a[1]+i.replace(et,"<$1></$2>")+a[2],l=a[0];while(l--)o=o.firstChild;x.merge(h,o.childNodes),o=p.firstChild,o.textContent=""}else h.push(t.createTextNode(i));p.textContent="",c=0;while(i=h[c++])if((!r||-1===x.inArray(i,r))&&(u=x.contains(i.ownerDocument,i),o=gt(p.appendChild(i),"script"),u&&ht(o),n)){l=0;while(i=o[l++])st.test(i.type||"")&&n.push(i)}return p},cleanData:function(e){var t,n,r,i=e.length,o=0,s=x.event.special;for(;i>o;o++){if(n=e[o],x.acceptData(n)&&(t=q.access(n)))for(r in t.events)s[r]?x.event.remove(n,r):x.removeEvent(n,r,t.handle);L.discard(n),q.discard(n)}},_evalUrl:function(e){return x.ajax({url:e,type:"GET",dataType:"text",async:!1,global:!1,success:x.globalEval})}});function ct(e,t){return x.nodeName(e,"table")&&x.nodeName(1===t.nodeType?t:t.firstChild,"tr")?e.getElementsByTagName("tbody")[0]||e.appendChild(e.ownerDocument.createElement("tbody")):e}function ft(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function pt(e){var t=at.exec(e.type);return t?e.type=t[1]:e.removeAttribute("type"),e}function ht(e,t){var n=e.length,r=0;for(;n>r;r++)q.set(e[r],"globalEval",!t||q.get(t[r],"globalEval"))}function dt(e,t){var n,r,i,o,s,a,u,l;if(1===t.nodeType){if(q.hasData(e)&&(o=q.access(e),s=x.extend({},o),l=o.events,q.set(t,s),l)){delete s.handle,s.events={};for(i in l)for(n=0,r=l[i].length;r>n;n++)x.event.add(t,i,l[i][n])}L.hasData(e)&&(a=L.access(e),u=x.extend({},a),L.set(t,u))}}function gt(e,t){var n=e.getElementsByTagName?e.getElementsByTagName(t||"*"):e.querySelectorAll?e.querySelectorAll(t||"*"):[];return t===undefined||t&&x.nodeName(e,t)?x.merge([e],n):n}function mt(e,t){var n=t.nodeName.toLowerCase();"input"===n&&it.test(e.type)?t.checked=e.checked:("input"===n||"textarea"===n)&&(t.defaultValue=e.defaultValue)}x.fn.extend({wrapAll:function(e){var t;return x.isFunction(e)?this.each(function(t){x(this).wrapAll(e.call(this,t))}):(this[0]&&(t=x(e,this[0].ownerDocument).eq(0).clone(!0),this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){var e=this;while(e.firstElementChild)e=e.firstElementChild;return e}).append(this)),this)},wrapInner:function(e){return x.isFunction(e)?this.each(function(t){x(this).wrapInner(e.call(this,t))}):this.each(function(){var t=x(this),n=t.contents();n.length?n.wrapAll(e):t.append(e)})},wrap:function(e){var t=x.isFunction(e);return this.each(function(n){x(this).wrapAll(t?e.call(this,n):e)})},unwrap:function(){return this.parent().each(function(){x.nodeName(this,"body")||x(this).replaceWith(this.childNodes)}).end()}});var yt,vt,xt=/^(none|table(?!-c[ea]).+)/,bt=/^margin/,wt=RegExp("^("+b+")(.*)$","i"),Tt=RegExp("^("+b+")(?!px)[a-z%]+$","i"),Ct=RegExp("^([+-])=("+b+")","i"),kt={BODY:"block"},Nt={position:"absolute",visibility:"hidden",display:"block"},Et={letterSpacing:0,fontWeight:400},St=["Top","Right","Bottom","Left"],jt=["Webkit","O","Moz","ms"];function Dt(e,t){if(t in e)return t;var n=t.charAt(0).toUpperCase()+t.slice(1),r=t,i=jt.length;while(i--)if(t=jt[i]+n,t in e)return t;return r}function At(e,t){return e=t||e,"none"===x.css(e,"display")||!x.contains(e.ownerDocument,e)}function Lt(t){return e.getComputedStyle(t,null)}function qt(e,t){var n,r,i,o=[],s=0,a=e.length;for(;a>s;s++)r=e[s],r.style&&(o[s]=q.get(r,"olddisplay"),n=r.style.display,t?(o[s]||"none"!==n||(r.style.display=""),""===r.style.display&&At(r)&&(o[s]=q.access(r,"olddisplay",Pt(r.nodeName)))):o[s]||(i=At(r),(n&&"none"!==n||!i)&&q.set(r,"olddisplay",i?n:x.css(r,"display"))));for(s=0;a>s;s++)r=e[s],r.style&&(t&&"none"!==r.style.display&&""!==r.style.display||(r.style.display=t?o[s]||"":"none"));return e}x.fn.extend({css:function(e,t){return x.access(this,function(e,t,n){var r,i,o={},s=0;if(x.isArray(t)){for(r=Lt(e),i=t.length;i>s;s++)o[t[s]]=x.css(e,t[s],!1,r);return o}return n!==undefined?x.style(e,t,n):x.css(e,t)},e,t,arguments.length>1)},show:function(){return qt(this,!0)},hide:function(){return qt(this)},toggle:function(e){var t="boolean"==typeof e;return this.each(function(){(t?e:At(this))?x(this).show():x(this).hide()})}}),x.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=yt(e,"opacity");return""===n?"1":n}}}},cssNumber:{columnCount:!0,fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":"cssFloat"},style:function(e,t,n,r){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var i,o,s,a=x.camelCase(t),u=e.style;return t=x.cssProps[a]||(x.cssProps[a]=Dt(u,a)),s=x.cssHooks[t]||x.cssHooks[a],n===undefined?s&&"get"in s&&(i=s.get(e,!1,r))!==undefined?i:u[t]:(o=typeof n,"string"===o&&(i=Ct.exec(n))&&(n=(i[1]+1)*i[2]+parseFloat(x.css(e,t)),o="number"),null==n||"number"===o&&isNaN(n)||("number"!==o||x.cssNumber[a]||(n+="px"),x.support.clearCloneStyle||""!==n||0!==t.indexOf("background")||(u[t]="inherit"),s&&"set"in s&&(n=s.set(e,n,r))===undefined||(u[t]=n)),undefined)}},css:function(e,t,n,r){var i,o,s,a=x.camelCase(t);return t=x.cssProps[a]||(x.cssProps[a]=Dt(e.style,a)),s=x.cssHooks[t]||x.cssHooks[a],s&&"get"in s&&(i=s.get(e,!0,n)),i===undefined&&(i=yt(e,t,r)),"normal"===i&&t in Et&&(i=Et[t]),""===n||n?(o=parseFloat(i),n===!0||x.isNumeric(o)?o||0:i):i}}),yt=function(e,t,n){var r,i,o,s=n||Lt(e),a=s?s.getPropertyValue(t)||s[t]:undefined,u=e.style;return s&&(""!==a||x.contains(e.ownerDocument,e)||(a=x.style(e,t)),Tt.test(a)&&bt.test(t)&&(r=u.width,i=u.minWidth,o=u.maxWidth,u.minWidth=u.maxWidth=u.width=a,a=s.width,u.width=r,u.minWidth=i,u.maxWidth=o)),a};function Ht(e,t,n){var r=wt.exec(t);return r?Math.max(0,r[1]-(n||0))+(r[2]||"px"):t}function Ot(e,t,n,r,i){var o=n===(r?"border":"content")?4:"width"===t?1:0,s=0;for(;4>o;o+=2)"margin"===n&&(s+=x.css(e,n+St[o],!0,i)),r?("content"===n&&(s-=x.css(e,"padding"+St[o],!0,i)),"margin"!==n&&(s-=x.css(e,"border"+St[o]+"Width",!0,i))):(s+=x.css(e,"padding"+St[o],!0,i),"padding"!==n&&(s+=x.css(e,"border"+St[o]+"Width",!0,i)));return s}function Ft(e,t,n){var r=!0,i="width"===t?e.offsetWidth:e.offsetHeight,o=Lt(e),s=x.support.boxSizing&&"border-box"===x.css(e,"boxSizing",!1,o);if(0>=i||null==i){if(i=yt(e,t,o),(0>i||null==i)&&(i=e.style[t]),Tt.test(i))return i;r=s&&(x.support.boxSizingReliable||i===e.style[t]),i=parseFloat(i)||0}return i+Ot(e,t,n||(s?"border":"content"),r,o)+"px"}function Pt(e){var t=o,n=kt[e];return n||(n=Rt(e,t),"none"!==n&&n||(vt=(vt||x("<iframe frameborder='0' width='0' height='0'/>").css("cssText","display:block !important")).appendTo(t.documentElement),t=(vt[0].contentWindow||vt[0].contentDocument).document,t.write("<!doctype html><html><body>"),t.close(),n=Rt(e,t),vt.detach()),kt[e]=n),n}function Rt(e,t){var n=x(t.createElement(e)).appendTo(t.body),r=x.css(n[0],"display");return n.remove(),r}x.each(["height","width"],function(e,t){x.cssHooks[t]={get:function(e,n,r){return n?0===e.offsetWidth&&xt.test(x.css(e,"display"))?x.swap(e,Nt,function(){return Ft(e,t,r)}):Ft(e,t,r):undefined},set:function(e,n,r){var i=r&&Lt(e);return Ht(e,n,r?Ot(e,t,r,x.support.boxSizing&&"border-box"===x.css(e,"boxSizing",!1,i),i):0)}}}),x(function(){x.support.reliableMarginRight||(x.cssHooks.marginRight={get:function(e,t){return t?x.swap(e,{display:"inline-block"},yt,[e,"marginRight"]):undefined}}),!x.support.pixelPosition&&x.fn.position&&x.each(["top","left"],function(e,t){x.cssHooks[t]={get:function(e,n){return n?(n=yt(e,t),Tt.test(n)?x(e).position()[t]+"px":n):undefined}}})}),x.expr&&x.expr.filters&&(x.expr.filters.hidden=function(e){return 0>=e.offsetWidth&&0>=e.offsetHeight},x.expr.filters.visible=function(e){return!x.expr.filters.hidden(e)}),x.each({margin:"",padding:"",border:"Width"},function(e,t){x.cssHooks[e+t]={expand:function(n){var r=0,i={},o="string"==typeof n?n.split(" "):[n];for(;4>r;r++)i[e+St[r]+t]=o[r]||o[r-2]||o[0];return i}},bt.test(e)||(x.cssHooks[e+t].set=Ht)});var Mt=/%20/g,Wt=/\[\]$/,$t=/\r?\n/g,Bt=/^(?:submit|button|image|reset|file)$/i,It=/^(?:input|select|textarea|keygen)/i;x.fn.extend({serialize:function(){return x.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var e=x.prop(this,"elements");return e?x.makeArray(e):this}).filter(function(){var e=this.type;return this.name&&!x(this).is(":disabled")&&It.test(this.nodeName)&&!Bt.test(e)&&(this.checked||!it.test(e))}).map(function(e,t){var n=x(this).val();return null==n?null:x.isArray(n)?x.map(n,function(e){return{name:t.name,value:e.replace($t,"\r\n")}}):{name:t.name,value:n.replace($t,"\r\n")}}).get()}}),x.param=function(e,t){var n,r=[],i=function(e,t){t=x.isFunction(t)?t():null==t?"":t,r[r.length]=encodeURIComponent(e)+"="+encodeURIComponent(t)};if(t===undefined&&(t=x.ajaxSettings&&x.ajaxSettings.traditional),x.isArray(e)||e.jquery&&!x.isPlainObject(e))x.each(e,function(){i(this.name,this.value)});else for(n in e)zt(n,e[n],t,i);return r.join("&").replace(Mt,"+")};function zt(e,t,n,r){var i;if(x.isArray(t))x.each(t,function(t,i){n||Wt.test(e)?r(e,i):zt(e+"["+("object"==typeof i?t:"")+"]",i,n,r)});else if(n||"object"!==x.type(t))r(e,t);else for(i in t)zt(e+"["+i+"]",t[i],n,r)}x.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error contextmenu".split(" "),function(e,t){x.fn[t]=function(e,n){return arguments.length>0?this.on(t,null,e,n):this.trigger(t)}}),x.fn.extend({hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)},bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)}});var _t,Xt,Ut=x.now(),Yt=/\?/,Vt=/#.*$/,Gt=/([?&])_=[^&]*/,Jt=/^(.*?):[ \t]*([^\r\n]*)$/gm,Qt=/^(?:about|app|app-storage|.+-extension|file|res|widget):$/,Kt=/^(?:GET|HEAD)$/,Zt=/^\/\//,en=/^([\w.+-]+:)(?:\/\/([^\/?#:]*)(?::(\d+)|)|)/,tn=x.fn.load,nn={},rn={},on="*/".concat("*");try{Xt=i.href}catch(sn){Xt=o.createElement("a"),Xt.href="",Xt=Xt.href}_t=en.exec(Xt.toLowerCase())||[];function an(e){return function(t,n){"string"!=typeof t&&(n=t,t="*");var r,i=0,o=t.toLowerCase().match(w)||[];
+if(x.isFunction(n))while(r=o[i++])"+"===r[0]?(r=r.slice(1)||"*",(e[r]=e[r]||[]).unshift(n)):(e[r]=e[r]||[]).push(n)}}function un(e,t,n,r){var i={},o=e===rn;function s(a){var u;return i[a]=!0,x.each(e[a]||[],function(e,a){var l=a(t,n,r);return"string"!=typeof l||o||i[l]?o?!(u=l):undefined:(t.dataTypes.unshift(l),s(l),!1)}),u}return s(t.dataTypes[0])||!i["*"]&&s("*")}function ln(e,t){var n,r,i=x.ajaxSettings.flatOptions||{};for(n in t)t[n]!==undefined&&((i[n]?e:r||(r={}))[n]=t[n]);return r&&x.extend(!0,e,r),e}x.fn.load=function(e,t,n){if("string"!=typeof e&&tn)return tn.apply(this,arguments);var r,i,o,s=this,a=e.indexOf(" ");return a>=0&&(r=e.slice(a),e=e.slice(0,a)),x.isFunction(t)?(n=t,t=undefined):t&&"object"==typeof t&&(i="POST"),s.length>0&&x.ajax({url:e,type:i,dataType:"html",data:t}).done(function(e){o=arguments,s.html(r?x("<div>").append(x.parseHTML(e)).find(r):e)}).complete(n&&function(e,t){s.each(n,o||[e.responseText,t,e])}),this},x.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){x.fn[t]=function(e){return this.on(t,e)}}),x.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:Xt,type:"GET",isLocal:Qt.test(_t[1]),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":on,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":x.parseJSON,"text xml":x.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(e,t){return t?ln(ln(e,x.ajaxSettings),t):ln(x.ajaxSettings,e)},ajaxPrefilter:an(nn),ajaxTransport:an(rn),ajax:function(e,t){"object"==typeof e&&(t=e,e=undefined),t=t||{};var n,r,i,o,s,a,u,l,c=x.ajaxSetup({},t),f=c.context||c,p=c.context&&(f.nodeType||f.jquery)?x(f):x.event,h=x.Deferred(),d=x.Callbacks("once memory"),g=c.statusCode||{},m={},y={},v=0,b="canceled",T={readyState:0,getResponseHeader:function(e){var t;if(2===v){if(!o){o={};while(t=Jt.exec(i))o[t[1].toLowerCase()]=t[2]}t=o[e.toLowerCase()]}return null==t?null:t},getAllResponseHeaders:function(){return 2===v?i:null},setRequestHeader:function(e,t){var n=e.toLowerCase();return v||(e=y[n]=y[n]||e,m[e]=t),this},overrideMimeType:function(e){return v||(c.mimeType=e),this},statusCode:function(e){var t;if(e)if(2>v)for(t in e)g[t]=[g[t],e[t]];else T.always(e[T.status]);return this},abort:function(e){var t=e||b;return n&&n.abort(t),k(0,t),this}};if(h.promise(T).complete=d.add,T.success=T.done,T.error=T.fail,c.url=((e||c.url||Xt)+"").replace(Vt,"").replace(Zt,_t[1]+"//"),c.type=t.method||t.type||c.method||c.type,c.dataTypes=x.trim(c.dataType||"*").toLowerCase().match(w)||[""],null==c.crossDomain&&(a=en.exec(c.url.toLowerCase()),c.crossDomain=!(!a||a[1]===_t[1]&&a[2]===_t[2]&&(a[3]||("http:"===a[1]?"80":"443"))===(_t[3]||("http:"===_t[1]?"80":"443")))),c.data&&c.processData&&"string"!=typeof c.data&&(c.data=x.param(c.data,c.traditional)),un(nn,c,t,T),2===v)return T;u=c.global,u&&0===x.active++&&x.event.trigger("ajaxStart"),c.type=c.type.toUpperCase(),c.hasContent=!Kt.test(c.type),r=c.url,c.hasContent||(c.data&&(r=c.url+=(Yt.test(r)?"&":"?")+c.data,delete c.data),c.cache===!1&&(c.url=Gt.test(r)?r.replace(Gt,"$1_="+Ut++):r+(Yt.test(r)?"&":"?")+"_="+Ut++)),c.ifModified&&(x.lastModified[r]&&T.setRequestHeader("If-Modified-Since",x.lastModified[r]),x.etag[r]&&T.setRequestHeader("If-None-Match",x.etag[r])),(c.data&&c.hasContent&&c.contentType!==!1||t.contentType)&&T.setRequestHeader("Content-Type",c.contentType),T.setRequestHeader("Accept",c.dataTypes[0]&&c.accepts[c.dataTypes[0]]?c.accepts[c.dataTypes[0]]+("*"!==c.dataTypes[0]?", "+on+"; q=0.01":""):c.accepts["*"]);for(l in c.headers)T.setRequestHeader(l,c.headers[l]);if(c.beforeSend&&(c.beforeSend.call(f,T,c)===!1||2===v))return T.abort();b="abort";for(l in{success:1,error:1,complete:1})T[l](c[l]);if(n=un(rn,c,t,T)){T.readyState=1,u&&p.trigger("ajaxSend",[T,c]),c.async&&c.timeout>0&&(s=setTimeout(function(){T.abort("timeout")},c.timeout));try{v=1,n.send(m,k)}catch(C){if(!(2>v))throw C;k(-1,C)}}else k(-1,"No Transport");function k(e,t,o,a){var l,m,y,b,w,C=t;2!==v&&(v=2,s&&clearTimeout(s),n=undefined,i=a||"",T.readyState=e>0?4:0,l=e>=200&&300>e||304===e,o&&(b=cn(c,T,o)),b=fn(c,b,T,l),l?(c.ifModified&&(w=T.getResponseHeader("Last-Modified"),w&&(x.lastModified[r]=w),w=T.getResponseHeader("etag"),w&&(x.etag[r]=w)),204===e?C="nocontent":304===e?C="notmodified":(C=b.state,m=b.data,y=b.error,l=!y)):(y=C,(e||!C)&&(C="error",0>e&&(e=0))),T.status=e,T.statusText=(t||C)+"",l?h.resolveWith(f,[m,C,T]):h.rejectWith(f,[T,C,y]),T.statusCode(g),g=undefined,u&&p.trigger(l?"ajaxSuccess":"ajaxError",[T,c,l?m:y]),d.fireWith(f,[T,C]),u&&(p.trigger("ajaxComplete",[T,c]),--x.active||x.event.trigger("ajaxStop")))}return T},getJSON:function(e,t,n){return x.get(e,t,n,"json")},getScript:function(e,t){return x.get(e,undefined,t,"script")}}),x.each(["get","post"],function(e,t){x[t]=function(e,n,r,i){return x.isFunction(n)&&(i=i||r,r=n,n=undefined),x.ajax({url:e,type:t,dataType:i,data:n,success:r})}});function cn(e,t,n){var r,i,o,s,a=e.contents,u=e.dataTypes;while("*"===u[0])u.shift(),r===undefined&&(r=e.mimeType||t.getResponseHeader("Content-Type"));if(r)for(i in a)if(a[i]&&a[i].test(r)){u.unshift(i);break}if(u[0]in n)o=u[0];else{for(i in n){if(!u[0]||e.converters[i+" "+u[0]]){o=i;break}s||(s=i)}o=o||s}return o?(o!==u[0]&&u.unshift(o),n[o]):undefined}function fn(e,t,n,r){var i,o,s,a,u,l={},c=e.dataTypes.slice();if(c[1])for(s in e.converters)l[s.toLowerCase()]=e.converters[s];o=c.shift();while(o)if(e.responseFields[o]&&(n[e.responseFields[o]]=t),!u&&r&&e.dataFilter&&(t=e.dataFilter(t,e.dataType)),u=o,o=c.shift())if("*"===o)o=u;else if("*"!==u&&u!==o){if(s=l[u+" "+o]||l["* "+o],!s)for(i in l)if(a=i.split(" "),a[1]===o&&(s=l[u+" "+a[0]]||l["* "+a[0]])){s===!0?s=l[i]:l[i]!==!0&&(o=a[0],c.unshift(a[1]));break}if(s!==!0)if(s&&e["throws"])t=s(t);else try{t=s(t)}catch(f){return{state:"parsererror",error:s?f:"No conversion from "+u+" to "+o}}}return{state:"success",data:t}}x.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/(?:java|ecma)script/},converters:{"text script":function(e){return x.globalEval(e),e}}}),x.ajaxPrefilter("script",function(e){e.cache===undefined&&(e.cache=!1),e.crossDomain&&(e.type="GET")}),x.ajaxTransport("script",function(e){if(e.crossDomain){var t,n;return{send:function(r,i){t=x("<script>").prop({async:!0,charset:e.scriptCharset,src:e.url}).on("load error",n=function(e){t.remove(),n=null,e&&i("error"===e.type?404:200,e.type)}),o.head.appendChild(t[0])},abort:function(){n&&n()}}}});var pn=[],hn=/(=)\?(?=&|$)|\?\?/;x.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=pn.pop()||x.expando+"_"+Ut++;return this[e]=!0,e}}),x.ajaxPrefilter("json jsonp",function(t,n,r){var i,o,s,a=t.jsonp!==!1&&(hn.test(t.url)?"url":"string"==typeof t.data&&!(t.contentType||"").indexOf("application/x-www-form-urlencoded")&&hn.test(t.data)&&"data");return a||"jsonp"===t.dataTypes[0]?(i=t.jsonpCallback=x.isFunction(t.jsonpCallback)?t.jsonpCallback():t.jsonpCallback,a?t[a]=t[a].replace(hn,"$1"+i):t.jsonp!==!1&&(t.url+=(Yt.test(t.url)?"&":"?")+t.jsonp+"="+i),t.converters["script json"]=function(){return s||x.error(i+" was not called"),s[0]},t.dataTypes[0]="json",o=e[i],e[i]=function(){s=arguments},r.always(function(){e[i]=o,t[i]&&(t.jsonpCallback=n.jsonpCallback,pn.push(i)),s&&x.isFunction(o)&&o(s[0]),s=o=undefined}),"script"):undefined}),x.ajaxSettings.xhr=function(){try{return new XMLHttpRequest}catch(e){}};var dn=x.ajaxSettings.xhr(),gn={0:200,1223:204},mn=0,yn={};e.ActiveXObject&&x(e).on("unload",function(){for(var e in yn)yn[e]();yn=undefined}),x.support.cors=!!dn&&"withCredentials"in dn,x.support.ajax=dn=!!dn,x.ajaxTransport(function(e){var t;return x.support.cors||dn&&!e.crossDomain?{send:function(n,r){var i,o,s=e.xhr();if(s.open(e.type,e.url,e.async,e.username,e.password),e.xhrFields)for(i in e.xhrFields)s[i]=e.xhrFields[i];e.mimeType&&s.overrideMimeType&&s.overrideMimeType(e.mimeType),e.crossDomain||n["X-Requested-With"]||(n["X-Requested-With"]="XMLHttpRequest");for(i in n)s.setRequestHeader(i,n[i]);t=function(e){return function(){t&&(delete yn[o],t=s.onload=s.onerror=null,"abort"===e?s.abort():"error"===e?r(s.status||404,s.statusText):r(gn[s.status]||s.status,s.statusText,"string"==typeof s.responseText?{text:s.responseText}:undefined,s.getAllResponseHeaders()))}},s.onload=t(),s.onerror=t("error"),t=yn[o=mn++]=t("abort"),s.send(e.hasContent&&e.data||null)},abort:function(){t&&t()}}:undefined});var vn,xn,bn=/^(?:toggle|show|hide)$/,wn=RegExp("^(?:([+-])=|)("+b+")([a-z%]*)$","i"),Tn=/queueHooks$/,Cn=[Dn],kn={"*":[function(e,t){var n,r,i=this.createTween(e,t),o=wn.exec(t),s=i.cur(),a=+s||0,u=1,l=20;if(o){if(n=+o[2],r=o[3]||(x.cssNumber[e]?"":"px"),"px"!==r&&a){a=x.css(i.elem,e,!0)||n||1;do u=u||".5",a/=u,x.style(i.elem,e,a+r);while(u!==(u=i.cur()/s)&&1!==u&&--l)}i.unit=r,i.start=a,i.end=o[1]?a+(o[1]+1)*n:n}return i}]};function Nn(){return setTimeout(function(){vn=undefined}),vn=x.now()}function En(e,t){x.each(t,function(t,n){var r=(kn[t]||[]).concat(kn["*"]),i=0,o=r.length;for(;o>i;i++)if(r[i].call(e,t,n))return})}function Sn(e,t,n){var r,i,o=0,s=Cn.length,a=x.Deferred().always(function(){delete u.elem}),u=function(){if(i)return!1;var t=vn||Nn(),n=Math.max(0,l.startTime+l.duration-t),r=n/l.duration||0,o=1-r,s=0,u=l.tweens.length;for(;u>s;s++)l.tweens[s].run(o);return a.notifyWith(e,[l,o,n]),1>o&&u?n:(a.resolveWith(e,[l]),!1)},l=a.promise({elem:e,props:x.extend({},t),opts:x.extend(!0,{specialEasing:{}},n),originalProperties:t,originalOptions:n,startTime:vn||Nn(),duration:n.duration,tweens:[],createTween:function(t,n){var r=x.Tween(e,l.opts,t,n,l.opts.specialEasing[t]||l.opts.easing);return l.tweens.push(r),r},stop:function(t){var n=0,r=t?l.tweens.length:0;if(i)return this;for(i=!0;r>n;n++)l.tweens[n].run(1);return t?a.resolveWith(e,[l,t]):a.rejectWith(e,[l,t]),this}}),c=l.props;for(jn(c,l.opts.specialEasing);s>o;o++)if(r=Cn[o].call(l,e,c,l.opts))return r;return En(l,c),x.isFunction(l.opts.start)&&l.opts.start.call(e,l),x.fx.timer(x.extend(u,{elem:e,anim:l,queue:l.opts.queue})),l.progress(l.opts.progress).done(l.opts.done,l.opts.complete).fail(l.opts.fail).always(l.opts.always)}function jn(e,t){var n,r,i,o,s;for(n in e)if(r=x.camelCase(n),i=t[r],o=e[n],x.isArray(o)&&(i=o[1],o=e[n]=o[0]),n!==r&&(e[r]=o,delete e[n]),s=x.cssHooks[r],s&&"expand"in s){o=s.expand(o),delete e[r];for(n in o)n in e||(e[n]=o[n],t[n]=i)}else t[r]=i}x.Animation=x.extend(Sn,{tweener:function(e,t){x.isFunction(e)?(t=e,e=["*"]):e=e.split(" ");var n,r=0,i=e.length;for(;i>r;r++)n=e[r],kn[n]=kn[n]||[],kn[n].unshift(t)},prefilter:function(e,t){t?Cn.unshift(e):Cn.push(e)}});function Dn(e,t,n){var r,i,o,s,a,u,l,c,f,p=this,h=e.style,d={},g=[],m=e.nodeType&&At(e);n.queue||(c=x._queueHooks(e,"fx"),null==c.unqueued&&(c.unqueued=0,f=c.empty.fire,c.empty.fire=function(){c.unqueued||f()}),c.unqueued++,p.always(function(){p.always(function(){c.unqueued--,x.queue(e,"fx").length||c.empty.fire()})})),1===e.nodeType&&("height"in t||"width"in t)&&(n.overflow=[h.overflow,h.overflowX,h.overflowY],"inline"===x.css(e,"display")&&"none"===x.css(e,"float")&&(h.display="inline-block")),n.overflow&&(h.overflow="hidden",p.always(function(){h.overflow=n.overflow[0],h.overflowX=n.overflow[1],h.overflowY=n.overflow[2]})),a=q.get(e,"fxshow");for(r in t)if(o=t[r],bn.exec(o)){if(delete t[r],u=u||"toggle"===o,o===(m?"hide":"show")){if("show"!==o||a===undefined||a[r]===undefined)continue;m=!0}g.push(r)}if(s=g.length){a=q.get(e,"fxshow")||q.access(e,"fxshow",{}),"hidden"in a&&(m=a.hidden),u&&(a.hidden=!m),m?x(e).show():p.done(function(){x(e).hide()}),p.done(function(){var t;q.remove(e,"fxshow");for(t in d)x.style(e,t,d[t])});for(r=0;s>r;r++)i=g[r],l=p.createTween(i,m?a[i]:0),d[i]=a[i]||x.style(e,i),i in a||(a[i]=l.start,m&&(l.end=l.start,l.start="width"===i||"height"===i?1:0))}}function An(e,t,n,r,i){return new An.prototype.init(e,t,n,r,i)}x.Tween=An,An.prototype={constructor:An,init:function(e,t,n,r,i,o){this.elem=e,this.prop=n,this.easing=i||"swing",this.options=t,this.start=this.now=this.cur(),this.end=r,this.unit=o||(x.cssNumber[n]?"":"px")},cur:function(){var e=An.propHooks[this.prop];return e&&e.get?e.get(this):An.propHooks._default.get(this)},run:function(e){var t,n=An.propHooks[this.prop];return this.pos=t=this.options.duration?x.easing[this.easing](e,this.options.duration*e,0,1,this.options.duration):e,this.now=(this.end-this.start)*t+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),n&&n.set?n.set(this):An.propHooks._default.set(this),this}},An.prototype.init.prototype=An.prototype,An.propHooks={_default:{get:function(e){var t;return null==e.elem[e.prop]||e.elem.style&&null!=e.elem.style[e.prop]?(t=x.css(e.elem,e.prop,""),t&&"auto"!==t?t:0):e.elem[e.prop]},set:function(e){x.fx.step[e.prop]?x.fx.step[e.prop](e):e.elem.style&&(null!=e.elem.style[x.cssProps[e.prop]]||x.cssHooks[e.prop])?x.style(e.elem,e.prop,e.now+e.unit):e.elem[e.prop]=e.now}}},An.propHooks.scrollTop=An.propHooks.scrollLeft={set:function(e){e.elem.nodeType&&e.elem.parentNode&&(e.elem[e.prop]=e.now)}},x.each(["toggle","show","hide"],function(e,t){var n=x.fn[t];x.fn[t]=function(e,r,i){return null==e||"boolean"==typeof e?n.apply(this,arguments):this.animate(Ln(t,!0),e,r,i)}}),x.fn.extend({fadeTo:function(e,t,n,r){return this.filter(At).css("opacity",0).show().end().animate({opacity:t},e,n,r)},animate:function(e,t,n,r){var i=x.isEmptyObject(e),o=x.speed(t,n,r),s=function(){var t=Sn(this,x.extend({},e),o);s.finish=function(){t.stop(!0)},(i||q.get(this,"finish"))&&t.stop(!0)};return s.finish=s,i||o.queue===!1?this.each(s):this.queue(o.queue,s)},stop:function(e,t,n){var r=function(e){var t=e.stop;delete e.stop,t(n)};return"string"!=typeof e&&(n=t,t=e,e=undefined),t&&e!==!1&&this.queue(e||"fx",[]),this.each(function(){var t=!0,i=null!=e&&e+"queueHooks",o=x.timers,s=q.get(this);if(i)s[i]&&s[i].stop&&r(s[i]);else for(i in s)s[i]&&s[i].stop&&Tn.test(i)&&r(s[i]);for(i=o.length;i--;)o[i].elem!==this||null!=e&&o[i].queue!==e||(o[i].anim.stop(n),t=!1,o.splice(i,1));(t||!n)&&x.dequeue(this,e)})},finish:function(e){return e!==!1&&(e=e||"fx"),this.each(function(){var t,n=q.get(this),r=n[e+"queue"],i=n[e+"queueHooks"],o=x.timers,s=r?r.length:0;for(n.finish=!0,x.queue(this,e,[]),i&&i.cur&&i.cur.finish&&i.cur.finish.call(this),t=o.length;t--;)o[t].elem===this&&o[t].queue===e&&(o[t].anim.stop(!0),o.splice(t,1));for(t=0;s>t;t++)r[t]&&r[t].finish&&r[t].finish.call(this);delete n.finish})}});function Ln(e,t){var n,r={height:e},i=0;for(t=t?1:0;4>i;i+=2-t)n=St[i],r["margin"+n]=r["padding"+n]=e;return t&&(r.opacity=r.width=e),r}x.each({slideDown:Ln("show"),slideUp:Ln("hide"),slideToggle:Ln("toggle"),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(e,t){x.fn[e]=function(e,n,r){return this.animate(t,e,n,r)}}),x.speed=function(e,t,n){var r=e&&"object"==typeof e?x.extend({},e):{complete:n||!n&&t||x.isFunction(e)&&e,duration:e,easing:n&&t||t&&!x.isFunction(t)&&t};return r.duration=x.fx.off?0:"number"==typeof r.duration?r.duration:r.duration in x.fx.speeds?x.fx.speeds[r.duration]:x.fx.speeds._default,(null==r.queue||r.queue===!0)&&(r.queue="fx"),r.old=r.complete,r.complete=function(){x.isFunction(r.old)&&r.old.call(this),r.queue&&x.dequeue(this,r.queue)},r},x.easing={linear:function(e){return e},swing:function(e){return.5-Math.cos(e*Math.PI)/2}},x.timers=[],x.fx=An.prototype.init,x.fx.tick=function(){var e,t=x.timers,n=0;for(vn=x.now();t.length>n;n++)e=t[n],e()||t[n]!==e||t.splice(n--,1);t.length||x.fx.stop(),vn=undefined},x.fx.timer=function(e){e()&&x.timers.push(e)&&x.fx.start()},x.fx.interval=13,x.fx.start=function(){xn||(xn=setInterval(x.fx.tick,x.fx.interval))},x.fx.stop=function(){clearInterval(xn),xn=null},x.fx.speeds={slow:600,fast:200,_default:400},x.fx.step={},x.expr&&x.expr.filters&&(x.expr.filters.animated=function(e){return x.grep(x.timers,function(t){return e===t.elem}).length}),x.fn.offset=function(e){if(arguments.length)return e===undefined?this:this.each(function(t){x.offset.setOffset(this,e,t)});var t,n,i=this[0],o={top:0,left:0},s=i&&i.ownerDocument;if(s)return t=s.documentElement,x.contains(t,i)?(typeof i.getBoundingClientRect!==r&&(o=i.getBoundingClientRect()),n=qn(s),{top:o.top+n.pageYOffset-t.clientTop,left:o.left+n.pageXOffset-t.clientLeft}):o},x.offset={setOffset:function(e,t,n){var r,i,o,s,a,u,l,c=x.css(e,"position"),f=x(e),p={};"static"===c&&(e.style.position="relative"),a=f.offset(),o=x.css(e,"top"),u=x.css(e,"left"),l=("absolute"===c||"fixed"===c)&&(o+u).indexOf("auto")>-1,l?(r=f.position(),s=r.top,i=r.left):(s=parseFloat(o)||0,i=parseFloat(u)||0),x.isFunction(t)&&(t=t.call(e,n,a)),null!=t.top&&(p.top=t.top-a.top+s),null!=t.left&&(p.left=t.left-a.left+i),"using"in t?t.using.call(e,p):f.css(p)}},x.fn.extend({position:function(){if(this[0]){var e,t,n=this[0],r={top:0,left:0};return"fixed"===x.css(n,"position")?t=n.getBoundingClientRect():(e=this.offsetParent(),t=this.offset(),x.nodeName(e[0],"html")||(r=e.offset()),r.top+=x.css(e[0],"borderTopWidth",!0),r.left+=x.css(e[0],"borderLeftWidth",!0)),{top:t.top-r.top-x.css(n,"marginTop",!0),left:t.left-r.left-x.css(n,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent||s;while(e&&!x.nodeName(e,"html")&&"static"===x.css(e,"position"))e=e.offsetParent;return e||s})}}),x.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,n){var r="pageYOffset"===n;x.fn[t]=function(i){return x.access(this,function(t,i,o){var s=qn(t);return o===undefined?s?s[n]:t[i]:(s?s.scrollTo(r?e.pageXOffset:o,r?o:e.pageYOffset):t[i]=o,undefined)},t,i,arguments.length,null)}});function qn(e){return x.isWindow(e)?e:9===e.nodeType&&e.defaultView}x.each({Height:"height",Width:"width"},function(e,t){x.each({padding:"inner"+e,content:t,"":"outer"+e},function(n,r){x.fn[r]=function(r,i){var o=arguments.length&&(n||"boolean"!=typeof r),s=n||(r===!0||i===!0?"margin":"border");return x.access(this,function(t,n,r){var i;return x.isWindow(t)?t.document.documentElement["client"+e]:9===t.nodeType?(i=t.documentElement,Math.max(t.body["scroll"+e],i["scroll"+e],t.body["offset"+e],i["offset"+e],i["client"+e])):r===undefined?x.css(t,n,s):x.style(t,n,r,s)},t,o?r:undefined,o,null)}})}),x.fn.size=function(){return this.length},x.fn.andSelf=x.fn.addBack,"object"==typeof module&&"object"==typeof module.exports?module.exports=x:"function"==typeof define&&define.amd&&define("jquery",[],function(){return x}),"object"==typeof e&&"object"==typeof e.document&&(e.jQuery=e.$=x)})(window);
\ No newline at end of file
--- a/web/res/js/live-polemic.js Wed Apr 10 18:38:21 2013 +0200
+++ b/web/res/js/live-polemic.js Fri Jun 07 11:55:46 2013 +0200
@@ -333,6 +333,9 @@
}
function flattenDateStruct(slices, target_level) {
+ if (!slices || !slices.length) {
+ return [];
+ }
var current_level = slices[0].level,
result = [];
if (current_level < target_level) {
@@ -356,6 +359,7 @@
function trimFDS() {
var slices = flattenDateStruct(twCx.timeline, twCx.timeLevel);
+ if (!slices.length) { return []; }
while (slices[0].tweets.length == 0) {
slices.splice(0,1);
}
@@ -504,7 +508,7 @@
}
function tlIdFromPos(x, y, outside) {
- if (!twCx.tlOnDisplay) {
+ if (!twCx.tlOnDisplay || !twCx.tlOnDisplay.length) {
return;
}
var ligne = Math.min( twCx.tlOnDisplay.length - 1, Math.max( 0, Math.floor(( twCx.tlHeight - y ) / twCx.scaleY) ) ),
@@ -815,6 +819,9 @@
}
twCx.tlOnDisplay = trimFDS();
+ if (!twCx.tlOnDisplay || !twCx.tlOnDisplay.length) {
+ return;
+ }
twCx.scaleY = twCx.tlHeight / twCx.tlOnDisplay.length;
var maxTweets = 0,
startTl = 0,
Binary file web/res/js/paper.js has changed
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/web/res/js/renkan.js Fri Jun 07 11:55:46 2013 +0200
@@ -0,0 +1,3483 @@
+ /* *********************************************************
+ File generated on Wed May 15 15:42:55 CEST 2013
+ ************************************************************
+ start of main.js
+ ********************************************************* */
+
+/*
+ _____ _
+ | __ \ | |
+ | |__) |___ _ __ | | ____ _ _ __
+ | _ // _ \ '_ \| |/ / _` | '_ \
+ | | \ \ __/ | | | < (_| | | | |
+ |_| \_\___|_| |_|_|\_\__,_|_| |_|
+
+ * Copyright 2012-2013 Institut de recherche et d'innovation
+ * contributor(s) : Yves-Marie Haussonne, Raphael Velt, Samuel Huron
+ *
+ * contact@iri.centrepompidou.fr
+ * http://www.iri.centrepompidou.fr
+ *
+ * This software is a computer program whose purpose is to show and add annotations on a video .
+ * This software is governed by the CeCILL-C license under French law and
+ * abiding by the rules of distribution of free software. You can use,
+ * modify and/ or redistribute the software under the terms of the CeCILL-C
+ * license as circulated by CEA, CNRS and INRIA at the following URL
+ * "http://www.cecill.info".
+ *
+ * The fact that you are presently reading this means that you have had
+ * knowledge of the CeCILL-C license and that you accept its terms.
+*/
+
+/* Declaring the Renkan Namespace Rkns and Default values */
+
+if (typeof Rkns !== "object") {
+ Rkns = {};
+}
+
+Rkns.$ = jQuery;
+
+Rkns._ = _;
+
+Rkns.VERSION = '0.2';
+
+Rkns.pickerColors = ["#8f1919", "#a80000", "#d82626", "#ff0000", "#e87c7c", "#ff6565", "#f7d3d3", "#fecccc",
+ "#8f5419", "#a85400", "#d87f26", "#ff7f00", "#e8b27c", "#ffb265", "#f7e5d3", "#fee5cc",
+ "#8f8f19", "#a8a800", "#d8d826", "#feff00", "#e8e87c", "#feff65", "#f7f7d3", "#fefecc",
+ "#198f19", "#00a800", "#26d826", "#00ff00", "#7ce87c", "#65ff65", "#d3f7d3", "#ccfecc",
+ "#198f8f", "#00a8a8", "#26d8d8", "#00feff", "#7ce8e8", "#65feff", "#d3f7f7", "#ccfefe",
+ "#19198f", "#0000a8", "#2626d8", "#0000ff", "#7c7ce8", "#6565ff", "#d3d3f7", "#ccccfe",
+ "#8f198f", "#a800a8", "#d826d8", "#ff00fe", "#e87ce8", "#ff65fe", "#f7d3f7", "#feccfe",
+ "#000000", "#242424", "#484848", "#6d6d6d", "#919191", "#b6b6b6", "#dadada", "#ffffff"];
+
+Rkns._BaseBin = function(_renkan, _opts) {
+ if (typeof _renkan !== "undefined") {
+ this.renkan = _renkan;
+ this.renkan.$.find(".Rk-Bin-Main").hide();
+ this.$ = Rkns.$('<li>')
+ .addClass("Rk-Bin")
+ .appendTo(_renkan.$.find(".Rk-Bin-List"));
+ this.title_icon_$ = Rkns.$('<span>')
+ .addClass("Rk-Bin-Title-Icon")
+ .appendTo(this.$);
+
+ var _this = this;
+
+ Rkns.$('<a>')
+ .attr({
+ href: "#",
+ title: _renkan.translate("Close bin")
+ })
+ .addClass("Rk-Bin-Close")
+ .html('×')
+ .appendTo(this.$)
+ .click(function() {
+ _this.destroy();
+ if (!_renkan.$.find(".Rk-Bin-Main:visible").length) {
+ _renkan.$.find(".Rk-Bin-Main:last").slideDown();
+ }
+ _renkan.resizeBins();
+ return false;
+ });
+ Rkns.$('<a>')
+ .attr({
+ href: "#",
+ title: _renkan.translate("Refresh bin")
+ })
+ .addClass("Rk-Bin-Refresh")
+ .appendTo(this.$)
+ .click(function() {
+ _this.refresh();
+ return false;
+ });
+ this.count_$ = Rkns.$('<div>')
+ .addClass("Rk-Bin-Count")
+ .appendTo(this.$);
+ this.title_$ = Rkns.$('<h2>')
+ .addClass("Rk-Bin-Title")
+ .appendTo(this.$);
+ this.main_$ = Rkns.$('<div>')
+ .addClass("Rk-Bin-Main")
+ .appendTo(this.$)
+ .html('<h4 class="Rk-Bin-Loading">' + _renkan.translate("Loading, please wait") + '</h4>');
+ this.title_$.html(_opts.title || '(new bin)');
+ this.renkan.resizeBins();
+
+ if (_opts.auto_refresh) {
+ window.setInterval(function() {
+ _this.refresh();
+ },_opts.auto_refresh);
+ }
+ }
+};
+
+Rkns._BaseBin.prototype.destroy = function() {
+ this.$.detach();
+ this.renkan.resizeBins();
+};
+
+/* Point of entry */
+
+Rkns.Renkan = function(_opts) {
+ var _this = this;
+
+ this.options = _.defaults(_opts, Rkns.defaults);
+
+ Rkns._(this.options.property_files).each(function(f) {
+ Rkns.$.getJSON(f, function(data) {
+ _this.options.properties = _this.options.properties.concat(data);
+ });
+ });
+
+ this.read_only = this.options.read_only || !this.options.editor_mode;
+
+ this.project = new Rkns.Models.Project();
+
+ if (typeof this.options.user_id !== "undefined") {
+ this.current_user = this.options.user_id;
+ }
+ this.$ = Rkns.$("#" + this.options.container);
+ this.$
+ .addClass("Rk-Main")
+ .html(this.template(this));
+ this.renderer = new Rkns.Renderer.Scene(this);
+ this.tabs = [];
+ this.search_engines = [];
+
+ this.current_user_list = new Rkns.Models.UsersList();
+
+ if (!this.options.search.length) {
+ this.$.find(".Rk-Web-Search-Form").detach();
+ } else {
+ var _tmpl = Rkns._.template('<li class="<%= className %>" data-key="<%= key %>"><%= title %></li>'),
+ _select = this.$.find(".Rk-Search-List"),
+ _input = this.$.find(".Rk-Web-Search-Input"),
+ _form = this.$.find(".Rk-Web-Search-Form");
+ Rkns._(this.options.search).each(function(_search, _key) {
+ if (Rkns[_search.type] && Rkns[_search.type].Search) {
+ _this.search_engines.push(new Rkns[_search.type].Search(_this, _search));
+ }
+ });
+ _select.html(
+ Rkns._(this.search_engines).map(function(_search, _key) {
+ return _tmpl({
+ key: _key,
+ title: _search.getSearchTitle(),
+ className: _search.getBgClass()
+ });
+ }).join("")
+ );
+ _select.find("li").click(function() {
+ var _el = Rkns.$(this);
+ _this.setSearchEngine(_el.attr("data-key"));
+ _form.submit();
+ });
+ _form.submit(function() {
+ if (_input.val()) {
+ var _search = _this.search_engine;
+ _search.search(_input.val());
+ }
+ return false;
+ });
+ this.$.find(".Rk-Search-Current").mouseenter(
+ function() { _select.slideDown(); }
+ );
+ this.$.find(".Rk-Search-Select").mouseleave(
+ function() { _select.hide(); }
+ );
+ this.setSearchEngine(0);
+ }
+ Rkns._(this.options.bins).each(function(_bin) {
+ if (Rkns[_bin.type] && Rkns[_bin.type].Bin) {
+ _this.tabs.push(new Rkns[_bin.type].Bin(_this, _bin));
+ }
+ });
+
+ var elementDropped = false;
+
+ this.$.find(".Rk-Bins")
+ .on("click",".Rk-Bin-Title,.Rk-Bin-Title-Icon", function() {
+ var _mainDiv = Rkns.$(this).siblings(".Rk-Bin-Main");
+ if (_mainDiv.is(":hidden")) {
+ _this.$.find(".Rk-Bin-Main").slideUp();
+ _mainDiv.slideDown();
+ }
+ }).on("mouseover", ".Rk-Bin-Item", function(_e) {
+ var _t = Rkns.$(this);
+ if (_t && $(_t).attr("data-uri")) {
+ var _models = _this.project.get("nodes").where({
+ uri: $(_t).attr("data-uri")
+ });
+ Rkns._(_models).each(function(_model) {
+ _this.renderer.highlightModel(_model);
+ });
+ }
+ }).mouseout(function() {
+ _this.renderer.unhighlightAll();
+ }).on("mousemove", ".Rk-Bin-Item", function(e) {
+ try {
+ this.dragDrop();
+ }
+ catch(err) {}
+ }).on("touchstart", ".Rk-Bin-Item", function(e) {
+ elementDropped = false;
+ }).on("touchmove", ".Rk-Bin-Item", function(e) {
+ e.preventDefault();
+ var touch = e.originalEvent.changedTouches[0],
+ off = _this.renderer.canvas_$.offset(),
+ w = _this.renderer.canvas_$.width(),
+ h = _this.renderer.canvas_$.height();
+ if (touch.pageX >= off.left && touch.pageX < (off.left + w) && touch.pageY >= off.top && touch.pageY < (off.top + h)) {
+ if (elementDropped) {
+ _this.renderer.onMouseMove(touch, true);
+ } else {
+ elementDropped = true;
+ var div = document.createElement('div');
+ div.appendChild(this.cloneNode(true));
+ _this.renderer.dropData({"text/html": div.innerHTML}, touch);
+ _this.renderer.onMouseDown(touch, true);
+ }
+ }
+ }).on("touchend", ".Rk-Bin-Item", function(e) {
+ if (elementDropped) {
+ _this.renderer.onMouseUp(e.originalEvent.changedTouches[0], true);
+ }
+ elementDropped = false;
+ }).on("dragstart", ".Rk-Bin-Item", function(e) {
+ var div = document.createElement('div');
+ div.appendChild(this.cloneNode(true));
+ try {
+ e.originalEvent.dataTransfer.setData("text/html",div.innerHTML);
+ }
+ catch(err) {
+ e.originalEvent.dataTransfer.setData("text",div.innerHTML);
+ }
+ });
+ Rkns.$(window).resize(function() {
+ _this.resizeBins();
+ });
+
+ this.$.find(".Rk-Bins-Search-Input").on("change keyup paste input", function() {
+ var val = Rkns.$(this).val();
+ Rkns._(_this.tabs).each(function(tab) {
+ tab.render(val);
+ });
+ });
+ this.$.find(".Rk-Bins-Search-Form").submit(function() {
+ return false;
+ });
+};
+
+Rkns.Renkan.prototype.template = Rkns._.template(
+ '<% if (options.show_bins) { %><div class="Rk-Bins"><div class="Rk-Bins-Head"><h2 class="Rk-Bins-Title"><%- translate("Select contents:")%></h2>'
+ + '<form class="Rk-Web-Search-Form Rk-Search-Form"><input class="Rk-Web-Search-Input Rk-Search-Input" type="search" placeholder="<%- translate("Search the Web") %>" />'
+ + '<div class="Rk-Search-Select"><div class="Rk-Search-Current"></div><ul class="Rk-Search-List"></ul></div>'
+ + '<input type="submit" value="" class="Rk-Web-Search-Submit Rk-Search-Submit" title="<%- translate("Search the Web") %>" /></form>'
+ + '<form class="Rk-Bins-Search-Form Rk-Search-Form"><input class="Rk-Bins-Search-Input Rk-Search-Input" type="search" placeholder="<%- translate("Search in Bins") %>" />'
+ + '<input type="submit" value="" class="Rk-Bins-Search-Submit Rk-Search-Submit" title="<%- translate("Search in Bins") %>" /></form></div>'
+ + '<ul class="Rk-Bin-List"></ul></div><% } %><div class="Rk-Render Rk-Render-<% if (options.show_bins) { %>Panel<% } else { %>Full<% } %>"></div>'
+);
+
+Rkns.Renkan.prototype.translate = function(_text) {
+ if (Rkns.i18n[this.options.language] && Rkns.i18n[this.options.language][_text]) {
+ return Rkns.i18n[this.options.language][_text];
+ }
+ if (this.options.language.length > 2 && Rkns.i18n[this.options.language.substr(0,2)] && Rkns.i18n[this.options.language.substr(0,2)][_text]) {
+ return Rkns.i18n[this.options.language.substr(0,2)][_text];
+ }
+ return _text;
+};
+
+Rkns.Renkan.prototype.onStatusChange = function() {
+ this.renderer.onStatusChange();
+};
+
+Rkns.Renkan.prototype.setSearchEngine = function(_key) {
+ this.search_engine = this.search_engines[_key];
+ this.$.find(".Rk-Search-Current").attr("class","Rk-Search-Current " + this.search_engine.getBgClass());
+};
+
+Rkns.Renkan.prototype.resizeBins = function() {
+ var _d = + this.$.find(".Rk-Bins-Head").outerHeight();
+ this.$.find(".Rk-Bin-Title:visible").each(function() {
+ _d += Rkns.$(this).outerHeight();
+ });
+ this.$.find(".Rk-Bin-Main").css({
+ height: this.$.find(".Rk-Bins").height() - _d
+ });
+};
+
+/* Utility functions */
+
+Rkns.Utils = {
+ _ID_AUTO_INCREMENT : 0,
+ _ID_BASE : (function(_d) {
+
+ function pad(n){return n<10 ? '0'+n : n;}
+ function fillrand(n) {
+ var _res = '';
+ for (var i=0; i<n; i++) {
+ _res += Math.floor(16*Math.random()).toString(16);
+ }
+ return _res;
+ }
+ return _d.getUTCFullYear() + '-'
+ + pad(_d.getUTCMonth()+1) + '-'
+ + pad(_d.getUTCDate()) + '-'
+ + fillrand(16);
+
+ })(new Date()),
+ getUID : function(_base) {
+
+ var _n = (++this._ID_AUTO_INCREMENT).toString(16),
+ _base = (typeof _base === "undefined" ? "" : _base + "-" );
+ while (_n.length < 4) {
+ _n = '0' + _n;
+ }
+ return _base + this._ID_BASE + '-' + _n;
+
+ },
+ getFullURL : function(url) {
+
+ if(typeof(url) == 'undefined' || url == null ) {
+ return "";
+ }
+ if(/https?:\/\//.test(url)) {
+ return url;
+ }
+ var img = new Image();
+ img.src = url;
+ var res = img.src;
+ img.src = null;
+ return res;
+
+ },
+ inherit : function(_baseClass, _callbefore) {
+
+ var _class = function(_arg) {
+ if (typeof _callbefore === "function") {
+ _callbefore.apply(this, Array.prototype.slice.call(arguments, 0));
+ }
+ _baseClass.apply(this, Array.prototype.slice.call(arguments, 0));
+ if (typeof this._init == "function" && !this._initialized) {
+ this._init.apply(this, Array.prototype.slice.call(arguments, 0));
+ this._initialized = true;
+ }
+ };
+ Rkns._(_class.prototype).extend(_baseClass.prototype);
+ return _class;
+
+ }
+};
+
+ /* *********************************************************
+ end of main.js
+ ************************************************************
+ ************************************************************
+ start of models.js
+ ********************************************************* */
+
+(function() {
+
+ var root = this;
+
+ var Backbone = root.Backbone;
+
+ var Models = root.Rkns.Models = {};
+
+
+ Models.getUID = function(obj) {
+ var guid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
+ var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8);
+ return v.toString(16);
+ });
+ return obj.type + "-" + guid;
+ };
+
+
+ var RenkanModel = Backbone.RelationalModel.extend({
+ idAttribute : "_id",
+ constructor: function(options) {
+
+ if (typeof options !== "undefined") {
+ options._id = options._id || options.id || Models.getUID(this);
+ options.title = options.title || "";
+ options.description = options.description || "";
+ options.uri = options.uri || "";
+
+ if(typeof this.prepare === "function") {
+ options = this.prepare(options);
+ }
+ }
+ Backbone.RelationalModel.prototype.constructor.call(this, options);
+ },
+ validate: function() {
+ if(!this.type) {
+ return "object has no type";
+ }
+ },
+ addReference : function(_options, _propName, _list, _id, _default) {
+ var _element = _list.get(_id);
+ if (typeof _element === "undefined" && typeof _default !== "undefined") {
+ _options[_propName ] = _default;
+ }
+ else {
+ _options[_propName ] = _element;
+ }
+ }
+ });
+
+ // USER
+ var User = Models.User = RenkanModel.extend({
+ type: "user",
+ prepare: function(options) {
+ options.color = options.color || "#666666";
+ return options;
+ },
+ toJSON: function() {
+ return {
+ _id: this.get("_id"),
+ title: this.get("title"),
+ uri: this.get("uri"),
+ description: this.get("description"),
+ color: this.get("color"),
+ };
+ },
+ });
+
+ // NODE
+ var Node = Models.Node = RenkanModel.extend({
+ type: "node",
+ relations: [{
+ type: Backbone.HasOne,
+ key: "created_by",
+ relatedModel: User
+ }],
+ prepare: function(options) {
+ project = options.project;
+ this.addReference(options, "created_by", project.get("users"), options.created_by, project.current_user);
+ options.description = options.description || "";
+ return options;
+ },
+ toJSON: function() {
+ return {
+ _id: this.get("_id"),
+ title: this.get("title"),
+ uri: this.get("uri"),
+ description: this.get("description"),
+ position: this.get("position"),
+ image: this.get("image"),
+ color: this.get("color"),
+ created_by: this.get("created_by") ? this.get("created_by").get("_id") : null,
+ size: this.get("size"),
+ "clip-path": this.get("clip-path")
+ };
+ },
+ });
+
+ // EDGE
+ var Edge = Models.Edge = RenkanModel.extend({
+ type: "edge",
+ relations: [
+ {
+ type: Backbone.HasOne,
+ key: "created_by",
+ relatedModel: User
+ },
+ {
+ type: Backbone.HasOne,
+ key: "from",
+ relatedModel: Node
+ },
+ {
+ type: Backbone.HasOne,
+ key: "to",
+ relatedModel: Node
+ },
+ ],
+ prepare: function(options) {
+ project = options.project;
+ this.addReference(options, "created_by", project.get("users"), options.created_by, project.current_user);
+ this.addReference(options, "from", project.get("nodes"), options.from);
+ this.addReference(options, "to", project.get("nodes"), options.to);
+ return options;
+ },
+ toJSON: function() {
+ return {
+ _id: this.get("_id"),
+ title: this.get("title"),
+ uri: this.get("uri"),
+ description: this.get("description"),
+ from: this.get("from") ? this.get("from").get("_id") : null,
+ to: this.get("to") ? this.get("to").get("_id") : null,
+ color: this.get("color"),
+ created_by: this.get("created_by") ? this.get("created_by").get("_id") : null
+ };
+ },
+ });
+
+ // PROJECT
+ var Project = Models.Project = RenkanModel.extend({
+ type: "project",
+ relations: [
+ {
+ type: Backbone.HasMany,
+ key: "users",
+ relatedModel: User,
+ reverseRelation: {
+ key: 'project',
+ includeInJSON: '_id'
+ },
+ },
+ {
+ type: Backbone.HasMany,
+ key: "nodes",
+ relatedModel: Node,
+ reverseRelation: {
+ key: 'project',
+ includeInJSON: '_id'
+ },
+ },
+ {
+ type: Backbone.HasMany,
+ key: "edges",
+ relatedModel: Edge,
+ reverseRelation: {
+ key: 'project',
+ includeInJSON: '_id'
+ },
+ }
+ ],
+ addUser: function(_props, _options) {
+ _props.project = this;
+ var _user = User.findOrCreate(_props);
+ this.get("users").push(_user, _options);
+ return _user;
+ },
+ addNode: function(_props, _options) {
+ _props.project = this;
+ var _node = Node.findOrCreate(_props);
+ this.get("nodes").push(_node, _options);
+ return _node;
+ },
+ addEdge: function(_props, _options) {
+ _props.project = this;
+ var _edge = Edge.findOrCreate(_props);
+ this.get("edges").push(_edge, _options);
+ return _edge;
+ },
+ removeNode: function(_model) {
+ this.get("nodes").remove(_model);
+ },
+ removeEdge: function(_model) {
+ this.get("edges").remove(_model);
+ },
+ validate: function(options) {
+ var _project = this;
+ _(options.users).each(function(_item) {
+ _item.project = _project;
+ });
+ _(options.nodes).each(function(_item) {
+ _item.project = _project;
+ });
+ _(options.edges).each(function(_item) {
+ _item.project = _project;
+ });
+ },
+ // Add event handler to remove edges when a node is removed
+ initialize: function() {
+ var _this = this;
+ this.on("remove:nodes", function(_node) {
+ _this.get("edges").remove(
+ _this.get("edges").filter(function(_edge) {
+ return _edge.get("from") == _node || _edge.get("to") == _node;
+ })
+ );
+ });
+ }
+ });
+
+ var RosterUser = Models.RosterUser = Backbone.Model.extend({
+ type: "roster_user",
+ idAttribute : "_id",
+
+ constructor: function(options) {
+
+ if (typeof options !== "undefined") {
+ options._id = options._id || options.id || Models.getUID(this);
+ options.title = options.title || "(untitled " + this.type + ")";
+ options.description = options.description || "";
+ options.uri = options.uri || "";
+ options.project = options.project || null;
+ options.site_id = options.site_id || 0;
+
+ if(typeof this.prepare === "function") {
+ options = this.prepare(options);
+ }
+ }
+ Backbone.Model.prototype.constructor.call(this, options);
+ },
+
+ validate: function() {
+ if(!this.type) {
+ return "object has no type";
+ }
+ },
+
+ prepare: function(options) {
+ options.color = options.color || "#666666";
+ return options;
+ },
+
+ toJSON: function() {
+ return {
+ _id: this.get("_id"),
+ title: this.get("title"),
+ uri: this.get("uri"),
+ description: this.get("description"),
+ color: this.get("color"),
+ project: (this.get("project") != null)?this.get("project").get("id"):null,
+ site_id: this.get("site_id")
+ };
+ },
+ });
+
+ var UsersList = Models.UsersList = Backbone.Collection.extend({
+ model: RosterUser
+ });
+
+
+}).call(window);
+
+
+ /* *********************************************************
+ end of models.js
+ ************************************************************
+ ************************************************************
+ start of defaults.js
+ ********************************************************* */
+
+Rkns.defaults = {
+
+ language: (navigator.language || navigator.userLanguage || "en"),
+ /* GUI Language */
+ container: "renkan",
+ /* GUI Container DOM element ID */
+ search: [],
+ /* List of Search Engines */
+ bins: [],
+ /* List of Bins */
+ static_url: "",
+ /* URL for static resources */
+ show_bins: true,
+ /* Show bins in left column */
+ properties: [],
+ /* Semantic properties for edges */
+ read_only: false,
+ /* Allows editing of renkan without changing the rest of the GUI. Can be switched on/off on the fly to block/enable editing */
+ editor_mode: true,
+ /* Switch for Publish/Edit GUI. If editor_mode is false, read_only will be true. */
+ snapshot_mode: false,
+ /* In snapshot mode, clicking on the floppy will save a snapshot. Otherwise, it will show the connection status */
+ show_top_bar: true,
+ /* Show the top bar, (title, buttons, users) */
+ default_user_color: "#303030",
+ size_bug_fix: true,
+ /* Resize the canvas after load (fixes a bug on iPad and FF Mac) */
+ force_resize: false,
+ allow_double_click: true,
+ /* Allows Double Click to create a node on an empty background */
+ element_delete_delay: 0,
+ /* Delay between clicking on the bin on an element and really deleting it
+ Set to 0 for delete confirm */
+ autoscale_padding: 50,
+
+ /* MINI-MAP OPTIONS */
+
+ show_minimap: true,
+ /* Show a small map at the bottom right */
+ minimap_width: 160,
+ minimap_height: 120,
+ minimap_padding: 20,
+ minimap_background_color: "#ffffff",
+ minimap_border_color: "#cccccc",
+ minimap_highlight_color: "#ffff00",
+ minimap_highlight_weight: 5,
+
+ /* EDGE/NODE COMMON OPTIONS */
+
+ buttons_background: "#202020",
+ buttons_label_color: "#c000c0",
+ buttons_label_font_size: 9,
+
+ /* NODE DISPLAY OPTIONS */
+
+ show_node_circles: true,
+ /* Show circles for nodes */
+ clip_node_images: true,
+ /* Constraint node images to circles */
+ node_images_fill_mode: false,
+ /* Set to false for "letterboxing" (height/width of node adapted to show full image)
+ Set to true for "crop" (adapted to fill circle) */
+ node_size_base: 25,
+ node_stroke_width: 2,
+ selected_node_stroke_width: 4,
+ node_fill_color: "#ffffff",
+ highlighted_node_fill_color: "#ffff00",
+ node_label_distance: 5,
+ /* Vertical distance between node and label */
+ node_label_max_length: 60,
+ /* Maximum displayed text length */
+ label_untitled_nodes: "(untitled)",
+ /* Label to display on untitled nodes */
+
+ /* EDGE DISPLAY OPTIONS */
+
+ edge_stroke_width: 2,
+ selected_edge_stroke_width: 4,
+ edge_label_distance: 0,
+ edge_label_max_length: 20,
+ edge_arrow_length: 18,
+ edge_arrow_width: 12,
+ edge_gap_in_bundles: 12,
+ label_untitled_edges: "",
+
+ /* CONTEXTUAL DISPLAY (TOOLTIP OR EDITOR) OPTIONS */
+
+ tooltip_width: 275,
+ tooltip_padding: 10,
+ tooltip_margin: 15,
+ tooltip_arrow_length : 20,
+ tooltip_arrow_width : 40,
+ tooltip_top_color: "#f0f0f0",
+ tooltip_bottom_color: "#d0d0d0",
+ tooltip_border_color: "#808080",
+ tooltip_border_width: 1,
+
+ /* NODE EDITOR OPTIONS */
+
+ show_node_editor_uri: true,
+ show_node_editor_description: true,
+ show_node_editor_size: true,
+ show_node_editor_color: true,
+ show_node_editor_image: true,
+ show_node_editor_creator: true,
+
+ /* NODE TOOLTIP OPTIONS */
+
+ show_node_tooltip_uri: true,
+ show_node_tooltip_description: true,
+ show_node_tooltip_color: true,
+ show_node_tooltip_image: true,
+ show_node_tooltip_creator: true,
+
+ /* EDGE EDITOR OPTIONS */
+
+ show_edge_editor_uri: true,
+ show_edge_editor_color: true,
+ show_edge_editor_direction: true,
+ show_edge_editor_nodes: true,
+ show_edge_editor_creator: true,
+
+ /* EDGE TOOLTIP OPTIONS */
+
+ show_edge_tooltip_uri: true,
+ show_edge_tooltip_color: true,
+ show_edge_tooltip_nodes: true,
+ show_edge_tooltip_creator: true
+
+ /* */
+
+};
+
+ /* *********************************************************
+ end of defaults.js
+ ************************************************************
+ ************************************************************
+ start of i18n.js
+ ********************************************************* */
+
+Rkns.i18n = {
+ fr: {
+ "Edit Node": "Édition d’un nœud",
+ "Edit Edge": "Édition d’un lien",
+ "Title:": "Titre :",
+ "URI:": "URI :",
+ "Description:": "Description :",
+ "From:": "De :",
+ "To:": "Vers :",
+ "Image": "Image",
+ "Image URL:": "URL d'Image",
+ "Choose Image File:": "Choisir un fichier image",
+ "Full Screen": "Mode plein écran",
+ "Add Node": "Ajouter un nœud",
+ "Add Edge": "Ajouter un lien",
+ "Archive Project": "Archiver le projet",
+ "Auto-save enabled": "Enregistrement automatique activé",
+ "Connection lost": "Connexion perdue",
+ "Created by:": "Créé par :",
+ "Zoom In": "Agrandir l’échelle",
+ "Zoom Out": "Rapetisser l’échelle",
+ "Edit": "Éditer",
+ "Remove": "Supprimer",
+ "Cancel deletion": "Annuler la suppression",
+ "Link to another node": "Créer un lien",
+ "Enlarge": "Agrandir",
+ "Shrink": "Rétrécir",
+ "Click on the background canvas to add a node": "Cliquer sur le fond du graphe pour rajouter un nœud",
+ "Click on a first node to start the edge": "Cliquer sur un premier nœud pour commencer le lien",
+ "Click on a second node to complete the edge": "Cliquer sur un second nœud pour terminer le lien",
+ "Twitter": "Twitter",
+ "Wikipedia": "Wikipédia",
+ "Wikipedia in ": "Wikipédia en ",
+ "French": "Français",
+ "English": "Anglais",
+ "Japanese": "Japonais",
+ "Untitled project": "Projet sans titre",
+ "Lignes de Temps": "Lignes de Temps",
+ "Loading, please wait": "Chargement en cours, merci de patienter",
+ "Edge color:": "Couleur :",
+ "Node color:": "Couleur :",
+ "Choose color": "Choisir une couleur",
+ "Change edge direction": "Changer le sens du lien",
+ "Do you really wish to remove node ": "Voulez-vous réellement supprimer le nœud ",
+ "Do you really wish to remove edge ": "Voulez-vous réellement supprimer le lien ",
+ "This file is not an image": "Ce fichier n'est pas une image",
+ "Image size must be under ": "L'image doit peser moins de ",
+ "Size:": "Taille :",
+ "KB": "ko",
+ "Choose from vocabulary:": "Choisir dans un vocabulaire :",
+ "SKOS Documentation properties": "SKOS: Propriétés documentaires",
+ "has note": "a pour note",
+ "has example": "a pour exemple",
+ "has definition": "a pour définition",
+ "SKOS Semantic relations": "SKOS: Relations sémantiques",
+ "has broader": "a pour concept plus large",
+ "has narrower": "a pour concept plus étroit",
+ "has related": "a pour concept apparenté",
+ "Dublin Core Metadata": "Métadonnées Dublin Core",
+ "has contributor": "a pour contributeur",
+ "covers": "couvre",
+ "created by": "créé par",
+ "has date": "a pour date",
+ "published by": "édité par",
+ "has source": "a pour source",
+ "has subject": "a pour sujet",
+ "Dragged resource": "Ressource glisée-déposée",
+ "Search the Web": "Rechercher en ligne",
+ "Search in Bins": "Rechercher dans les chutiers",
+ "Close bin": "Fermer le chutier",
+ "Refresh bin": "Rafraîchir le chutier",
+ "(untitled)": "(sans titre)",
+ "Select contents:": "Sélectionner des contenus :",
+ "Drag items from this website, drop them in Renkan": "Glissez des éléments de ce site web vers Renkan",
+ "Drag this button to your bookmark bar. When on a third-party website, click it to enable drag-and-drop from the website to Renkan.": "Glissez ce bouton vers votre barre de favoris. Ensuite, depuis un site tiers, cliquez dessus pour activer 'Drag-to-Add' puis glissez des éléments de ce site vers Renkan"
+ }
+}
+
+ /* *********************************************************
+ end of i18n.js
+ ************************************************************
+ ************************************************************
+ start of paper-renderer.js
+ ********************************************************* */
+
+Rkns.Renderer = {
+ _MIN_DRAG_DISTANCE: 2,
+ _NODE_BUTTON_WIDTH: 40,
+ _EDGE_BUTTON_INNER: 2,
+ _EDGE_BUTTON_OUTER: 40,
+ _CLICKMODE_ADDNODE : 1,
+ _CLICKMODE_STARTEDGE : 2,
+ _CLICKMODE_ENDEDGE : 3,
+ _IMAGE_MAX_KB : 500,
+ _NODE_SIZE_STEP: Math.LN2/4,
+ _MIN_SCALE: 1/20,
+ _MAX_SCALE: 20,
+ _MOUSEMOVE_RATE: 80,
+ _DOUBLETAP_DELAY: 800,
+ _DOUBLETAP_DISTANCE: 20*20,
+ _USER_PLACEHOLDER : function(_renkan) {
+ return {
+ color: _renkan.options.default_user_color,
+ title: _renkan.translate("(unknown user)"),
+ get: function(attr) {
+ return this[attr] || false;
+ }
+ };
+ },
+ _BOOKMARKLET_CODE: function(_renkan) {
+ return "(function(a,b,c,d,e,f,h,i,j,k,l,m,n,o,p,q,r){a=document;b=a.body;c=a.location.href;j='draggable';m='text/x-iri-';d=a.createElement('div');d.innerHTML='<p_style=\"position:fixed;top:0;right:0;font:bold_18px_sans-serif;color:#fff;background:#909;padding:10px;z-index:100000;\">"
+ + _renkan.translate("Drag items from this website, drop them in Renkan").replace(/ /g,"_")
+ + "</p>'.replace(/_/g,String.fromCharCode(32));b.appendChild(d);e=[{r:/https?:\\/\\/[^\\/]*twitter\\.com\\//,s:'.tweet',n:'twitter'},{r:/https?:\\/\\/[^\\/]*google\\.[^\\/]+\\//,s:'.g',n:'google'},{r:/https?:\\/\\/[^\\/]*lemonde\\.fr\\//,s:'[data-vr-contentbox]',n:'lemonde'}];f=false;e.forEach(function(g){if(g.r.test(c)){f=g;}});if(f){h=function(){Array.prototype.forEach.call(a.querySelectorAll(f.s),function(i){i[j]=true;k=i.style;k.borderWidth='2px';k.borderColor='#909';k.borderStyle='solid';k.backgroundColor='rgba(200,0,180,.1)';})};window.setInterval(h,500);h();};a.addEventListener('dragstart',function(k){l=k.dataTransfer;l.setData(m+'source-uri',c);l.setData(m+'source-title',a.title);n=k.target;if(f){o=n;while(!o.attributes[j]){o=o.parentNode;if(o==b){break;}}}if(f&&o.attributes[j]){p=o.cloneNode(true);l.setData(m+'specific-site',f.n)}else{q=a.getSelection();if(q.type==='Range'||!q.type){p=q.getRangeAt(0).cloneContents();}else{p=n.cloneNode();}}r=a.createElement('div');r.appendChild(p);l.setData('text/x-iri-selected-text',r.textContent.trim());l.setData('text/x-iri-selected-html',r.innerHTML);},false);})();";
+ },
+ shortenText : function(_text, _maxlength) {
+ return (_text.length > _maxlength ? (_text.substr(0,_maxlength) + '…') : _text);
+ },
+ drawEditBox : function(_options, _coords, _path, _xmargin, _selector) {
+ _selector.css({
+ width: ( _options.tooltip_width - 2* _options.tooltip_padding ),
+ });
+ var _height = _selector.outerHeight() + 2* _options.tooltip_padding,
+ _isLeft = (_coords.x < paper.view.center.x ? 1 : -1),
+ _left = _coords.x + _isLeft * ( _xmargin + _options.tooltip_arrow_length ),
+ _right = _coords.x + _isLeft * ( _xmargin + _options.tooltip_arrow_length + _options.tooltip_width ),
+ _top = _coords.y - _height / 2;
+ if (_top + _height > (paper.view.size.height - _options.tooltip_margin)) {
+ _top = Math.max( paper.view.size.height - _options.tooltip_margin, _coords.y + _options.tooltip_arrow_width / 2 ) - _height;
+ }
+ if (_top < _options.tooltip_margin) {
+ _top = Math.min( _options.tooltip_margin, _coords.y - _options.tooltip_arrow_width / 2 );
+ }
+ var _bottom = _top + _height;
+ _path.segments[0].point
+ = _path.segments[7].point
+ = _coords.add([_isLeft * _xmargin, 0]);
+ _path.segments[1].point.x
+ = _path.segments[2].point.x
+ = _path.segments[5].point.x
+ = _path.segments[6].point.x
+ = _left;
+ _path.segments[3].point.x
+ = _path.segments[4].point.x
+ = _right;
+ _path.segments[2].point.y
+ = _path.segments[3].point.y
+ = _top;
+ _path.segments[4].point.y
+ = _path.segments[5].point.y
+ = _bottom;
+ _path.segments[1].point.y = _coords.y - _options.tooltip_arrow_width / 2;
+ _path.segments[6].point.y = _coords.y + _options.tooltip_arrow_width / 2;
+ _path.closed = true;
+ _path.fillColor = new paper.GradientColor(new paper.Gradient([_options.tooltip_top_color, _options.tooltip_bottom_color]), [0,_top], [0, _bottom]);
+ _selector.css({
+ left: (_options.tooltip_padding + Math.min(_left, _right)),
+ top: (_options.tooltip_padding + _top)
+ });
+ return _path;
+ }
+};
+
+Rkns.Renderer._BaseRepresentation = function(_renderer, _model) {
+ if (typeof _renderer !== "undefined") {
+ this.renderer = _renderer;
+ this.renkan = _renderer.renkan;
+ this.project = _renderer.renkan.project;
+ this.options = _renderer.renkan.options;
+ this.model = _model;
+ if (this.model) {
+ var _this = this;
+ this._changeBinding = function() {
+ _this.redraw();
+ };
+ this._removeBinding = function() {
+ _renderer.removeRepresentation(_this);
+ _(function() {
+ _renderer.redraw();
+ }).defer();
+ };
+ this._selectBinding = function() {
+ _this.select();
+ };
+ this._unselectBinding = function() {
+ _this.unselect();
+ };
+ this.model.on("change", this._changeBinding );
+ this.model.on("remove", this._removeBinding );
+ this.model.on("select", this._selectBinding );
+ this.model.on("unselect", this._unselectBinding );
+ }
+ }
+};
+
+Rkns.Renderer._BaseRepresentation.prototype.super = function(_func) {
+ Rkns.Renderer._BaseRepresentation.prototype[_func].apply(this, Array.prototype.slice.call(arguments, 1));
+};
+
+Rkns.Renderer._BaseRepresentation.prototype.redraw = function() {};
+
+Rkns.Renderer._BaseRepresentation.prototype.moveTo = function() {};
+
+Rkns.Renderer._BaseRepresentation.prototype.show = function() {};
+
+Rkns.Renderer._BaseRepresentation.prototype.hide = function() {};
+
+Rkns.Renderer._BaseRepresentation.prototype.select = function() {
+ if (this.model) {
+ this.model.trigger("selected");
+ }
+};
+
+Rkns.Renderer._BaseRepresentation.prototype.unselect = function() {
+ if (this.model) {
+ this.model.trigger("unselected");
+ }
+};
+
+Rkns.Renderer._BaseRepresentation.prototype.highlight = function() {};
+
+Rkns.Renderer._BaseRepresentation.prototype.unhighlight = function() {};
+
+Rkns.Renderer._BaseRepresentation.prototype.mousedown = function() {};
+
+Rkns.Renderer._BaseRepresentation.prototype.mouseup = function() {
+ if (this.model) {
+ this.model.trigger("clicked");
+ }
+};
+
+Rkns.Renderer._BaseRepresentation.prototype.destroy = function() {
+ if (this.model) {
+ this.model.off("change", this._changeBinding );
+ this.model.off("remove", this._removeBinding );
+ this.model.off("select", this._selectBinding );
+ this.model.off("unselect", this._unselectBinding );
+ }
+};
+
+/* */
+
+Rkns.Renderer._BaseButton = Rkns.Utils.inherit(Rkns.Renderer._BaseRepresentation);
+
+Rkns.Renderer._BaseButton.prototype.moveTo = function(_pos) {
+ this.sector.moveTo(_pos);
+};
+
+Rkns.Renderer._BaseButton.prototype.show = function() {
+ this.sector.show();
+};
+
+Rkns.Renderer._BaseButton.prototype.hide = function() {
+ this.sector.hide();
+};
+
+Rkns.Renderer._BaseButton.prototype.select = function() {
+ this.sector.select();
+};
+
+Rkns.Renderer._BaseButton.prototype.unselect = function(_newTarget) {
+ this.sector.unselect();
+ if (!_newTarget || (_newTarget !== this.source_representation && _newTarget.source_representation !== this.source_representation)) {
+ this.source_representation.unselect();
+ }
+};
+
+Rkns.Renderer._BaseButton.prototype.destroy = function() {
+ this.sector.destroy();
+};
+
+/* */
+
+Rkns.Renderer.Node = Rkns.Utils.inherit(Rkns.Renderer._BaseRepresentation);
+
+Rkns.Renderer.Node.prototype._init = function() {
+ this.renderer.node_layer.activate();
+ this.type = "Node";
+ this.circle = new paper.Path.Circle([0, 0], 1);
+ this.circle.__representation = this;
+ if (this.options.show_node_circles) {
+ this.circle.strokeWidth = this.options.node_stroke_width;
+ this.h_ratio = 1;
+ } else {
+ this.h_ratio = 0;
+ }
+ this.title = Rkns.$('<div class="Rk-Label">').appendTo(this.renderer.labels_$);
+ if (this.options.editor_mode) {
+ this.normal_buttons = [
+ new Rkns.Renderer.NodeEditButton(this.renderer, null),
+ new Rkns.Renderer.NodeRemoveButton(this.renderer, null),
+ new Rkns.Renderer.NodeLinkButton(this.renderer, null),
+ new Rkns.Renderer.NodeEnlargeButton(this.renderer, null),
+ new Rkns.Renderer.NodeShrinkButton(this.renderer, null)
+ ];
+ this.pending_delete_buttons = [
+ new Rkns.Renderer.NodeRevertButton(this.renderer, null)
+ ];
+ this.all_buttons = this.normal_buttons.concat(this.pending_delete_buttons);
+ for (var i = 0; i < this.all_buttons.length; i++) {
+ this.all_buttons[i].source_representation = this;
+ }
+ this.active_buttons = [];
+ } else {
+ this.active_buttons = this.all_buttons = [];
+ }
+ this.last_circle_radius = 1;
+
+ if (this.renderer.minimap) {
+ this.renderer.minimap.node_layer.activate();
+ this.minimap_circle = new paper.Path.Circle([0, 0], 1);
+ this.minimap_circle.__representation = this.renderer.minimap.miniframe.__representation;
+ this.renderer.minimap.node_group.addChild(this.minimap_circle);
+ }
+};
+
+Rkns.Renderer.Node.prototype.redraw = function(_dontRedrawEdges) {
+ var _model_coords = new paper.Point(this.model.get("position")),
+ _baseRadius = this.options.node_size_base * Math.exp((this.model.get("size") || 0) * Rkns.Renderer._NODE_SIZE_STEP);
+ if (!this.is_dragging || !this.paper_coords) {
+ this.paper_coords = this.renderer.toPaperCoords(_model_coords);
+ }
+ this.circle_radius = _baseRadius * this.renderer.scale;
+ if (this.last_circle_radius !== this.circle_radius) {
+ this.all_buttons.forEach(function(b) {
+ b.setSectorSize();
+ });
+ var square = new paper.Size(this.circle_radius, this.circle_radius),
+ topleft = this.paper_coords.subtract(square),
+ bounds = new paper.Rectangle(topleft, square.multiply(2));
+ this.circle.scale(this.circle_radius / this.last_circle_radius);
+ if (this.node_image) {
+ this.node_image.scale(this.circle_radius / this.last_circle_radius);
+ }
+ }
+ this.circle.position = this.paper_coords;
+ if (this.node_image) {
+ this.node_image.position = this.paper_coords.subtract(this.image_delta.multiply(this.circle_radius));
+ }
+ this.last_circle_radius = this.circle_radius;
+
+ var old_act_btn = this.active_buttons;
+
+ if (this.model.get("delete_scheduled")) {
+ var opacity = .5;
+ this.active_buttons = this.pending_delete_buttons;
+ this.circle.dashArray = [2,2];
+ } else {
+ var opacity = 1;
+ this.active_buttons = this.normal_buttons;
+ this.circle.dashArray = null;
+ }
+
+ if (this.selected && this.renderer.isEditable()) {
+ if (old_act_btn !== this.active_buttons) {
+ old_act_btn.forEach(function(b) {
+ b.hide();
+ });
+ }
+ this.active_buttons.forEach(function(b) {
+ b.show();
+ });
+ }
+
+ if (this.node_image) {
+ this.node_image.opacity = this.highlighted ? opacity * .5 : (opacity - .01);
+ }
+
+ this.circle.fillColor = this.highlighted ? this.options.highlighted_node_fill_color : this.options.node_fill_color;
+
+ this.circle.opacity = this.options.show_node_circles ? opacity : .01;
+
+ var _text = this.model.get("title") || this.renkan.translate(this.options.label_untitled_nodes) || "";
+ _text = Rkns.Renderer.shortenText(_text, this.options.node_label_max_length);
+ this.title.text(_text);
+ this.title.css({
+ left: this.paper_coords.x,
+ top: this.paper_coords.y + this.circle_radius * this.h_ratio + this.options.node_label_distance,
+ opacity: opacity
+ });
+ var _color = this.model.get("color") || (this.model.get("created_by") || Rkns.Renderer._USER_PLACEHOLDER(this.renkan)).get("color");
+ this.circle.strokeColor = _color;
+ var _pc = this.paper_coords;
+ this.all_buttons.forEach(function(b) {
+ b.moveTo(_pc);
+ });
+ var lastImage = this.img;
+ this.img = this.model.get("image");
+ if (this.img && this.img !== lastImage) {
+ this.showImage();
+ }
+ if (this.node_image && !this.img) {
+ this.node_image.remove();
+ delete this.node_image;
+ }
+
+ if (this.renderer.minimap) {
+ this.minimap_circle.fillColor = _color;
+ var minipos = this.renderer.toMinimapCoords(_model_coords),
+ miniradius = this.renderer.minimap.scale * _baseRadius,
+ minisize = new paper.Size([miniradius, miniradius]);
+ this.minimap_circle.fitBounds(minipos.subtract(minisize), minisize.multiply(2));
+ }
+
+ if (!_dontRedrawEdges) {
+ Rkns._.each(this.project.get("edges").filter(function (ed) { return ((ed.to === this.model) || (ed.from === this.model));}), function(edge, index, list){
+ var repr = this.renderer.getRepresentationByModel(edge);
+ if (repr && typeof repr.from_representation !== "undefined" && typeof repr.from_representation.paper_coords !== "undefined" && typeof repr.to_representation !== "undefined" && typeof repr.to_representation.paper_coords !== "undefined") {
+ repr.redraw();
+ }
+ }, this);
+ }
+
+};
+
+Rkns.Renderer.Node.prototype.showImage = function() {
+ if (typeof this.renderer.image_cache[this.img] === "undefined") {
+ var _image = new Image();
+ this.renderer.image_cache[this.img] = _image;
+ _image.src = this.img;
+ } else {
+ var _image = this.renderer.image_cache[this.img];
+ }
+ if (_image.width) {
+ if (this.node_image) {
+ this.node_image.remove();
+ }
+ this.renderer.node_layer.activate();
+ var width = _image.width,
+ height = _image.height,
+ clipPath = this.model.get("clip-path"),
+ hasClipPath = (typeof clipPath !== "undefined" && clipPath);
+ if (hasClipPath) {
+ var _clip = new paper.Path(),
+ instructions = clipPath.match(/[a-z][^a-z]+/gi) || [],
+ lastCoords = [0,0],
+ minX = Infinity,
+ minY = Infinity,
+ maxX = -Infinity,
+ maxY = -Infinity;
+
+ function transformCoords(tabc, relative) {
+ var newCoords = tabc.slice(1).map(function(v, k) {
+ var res = parseFloat(v),
+ isY = k % 2;
+ if (isY) {
+ res = ( res - .5 ) * height;
+ } else {
+ res = ( res - .5 ) * width;
+ }
+ if (relative) {
+ res += lastCoords[isY];
+ }
+ if (isY) {
+ minY = Math.min(minY, res);
+ maxY = Math.max(maxY, res);
+ } else {
+ minX = Math.min(minX, res);
+ maxX = Math.max(maxX, res);
+ }
+ return res;
+ });
+ lastCoords = newCoords.slice(-2);
+ return newCoords;
+ }
+
+ instructions.forEach(function(instr) {
+ var coords = instr.match(/([a-z]|[0-9.-]+)/ig) || [""];
+ switch(coords[0]) {
+ case "M":
+ _clip.moveTo(transformCoords(coords));
+ break;
+ case "m":
+ _clip.moveTo(transformCoords(coords, true));
+ break;
+ case "L":
+ _clip.lineTo(transformCoords(coords));
+ break;
+ case "l":
+ _clip.lineTo(transformCoords(coords, true));
+ break;
+ case "C":
+ _clip.cubicCurveTo(transformCoords(coords));
+ break;
+ case "c":
+ _clip.cubicCurveTo(transformCoords(coords, true));
+ break;
+ case "Q":
+ _clip.quadraticCurveTo(transformCoords(coords));
+ break;
+ case "q":
+ _clip.quadraticCurveTo(transformCoords(coords, true));
+ break;
+ }
+ });
+
+ var baseRadius = Math[this.options.node_images_fill_mode ? "min" : "max"](maxX - minX, maxY - minY) / 2,
+ centerPoint = new paper.Point((maxX + minX) / 2, (maxY + minY) / 2);
+ if (!this.options.show_node_circles) {
+ this.h_ratio = (maxY - minY) / (2 * baseRadius);
+ }
+ } else {
+ var baseRadius = Math[this.options.node_images_fill_mode ? "min" : "max"](width, height) / 2,
+ centerPoint = new paper.Point(0,0);
+ if (!this.options.show_node_circles) {
+ this.h_ratio = height / (2 * baseRadius);
+ }
+ }
+ var _raster = new paper.Raster(_image);
+ if (hasClipPath) {
+ _raster = new paper.Group(_clip, _raster);
+ _raster.opacity = .99;
+ /* This is a workaround to allow clipping at group level
+ * If opacity was set to 1, paper.js would merge all clipping groups in one (known bug).
+ */
+ _raster.clipped = true;
+ _clip.__representation = this;
+ }
+ if (this.options.clip_node_images) {
+ var _circleClip = new paper.Path.Circle(centerPoint, baseRadius);
+ _raster = new paper.Group(_circleClip, _raster);
+ _raster.opacity = .99;
+ _raster.clipped = true;
+ _circleClip.__representation = this;
+ }
+ this.image_delta = centerPoint.divide(baseRadius);
+ this.node_image = _raster;
+ this.node_image.__representation = _this;
+ this.node_image.scale(this.circle_radius / baseRadius);
+ this.node_image.position = this.paper_coords.subtract(this.image_delta.multiply(this.circle_radius));
+ this.redraw();
+ this.renderer.throttledPaperDraw();
+ } else {
+ var _this = this;
+ Rkns.$(_image).on("load", function() {
+ _this.showImage();
+ });
+ }
+}
+
+Rkns.Renderer.Node.prototype.paperShift = function(_delta) {
+ if (this.options.editor_mode) {
+ if (!this.renkan.read_only) {
+ this.is_dragging = true;
+ this.paper_coords = this.paper_coords.add(_delta);
+ this.redraw();
+ }
+ } else {
+ this.renderer.paperShift(_delta);
+ }
+};
+
+Rkns.Renderer.Node.prototype.openEditor = function() {
+ this.renderer.removeRepresentationsOfType("editor");
+ var _editor = this.renderer.addRepresentation("NodeEditor",null);
+ _editor.source_representation = this;
+ _editor.draw();
+};
+
+Rkns.Renderer.Node.prototype.select = function() {
+ this.selected = true;
+ this.circle.strokeWidth = this.options.selected_node_stroke_width;
+ if (this.renderer.isEditable()) {
+ this.active_buttons.forEach(function(b) {
+ b.show();
+ });
+ }
+ var _uri = this.model.get("uri");
+ if (_uri) {
+ Rkns.$('.Rk-Bin-Item').each(function() {
+ var _el = Rkns.$(this);
+ if (_el.attr("data-uri") == _uri) {
+ _el.addClass("selected");
+ }
+ });
+ }
+ if (!this.options.editor_mode) {
+ this.openEditor();
+ }
+
+ if (this.renderer.minimap) {
+ this.minimap_circle.strokeWidth = this.options.minimap_highlight_weight;
+ this.minimap_circle.strokeColor = this.options.minimap_highlight_color;
+ }
+ this.super("select");
+};
+
+Rkns.Renderer.Node.prototype.unselect = function(_newTarget) {
+ if (!_newTarget || _newTarget.source_representation !== this) {
+ this.selected = false;
+ this.all_buttons.forEach(function(b) {
+ b.hide();
+ });
+ this.circle.strokeWidth = this.options.node_stroke_width;
+ Rkns.$('.Rk-Bin-Item').removeClass("selected");
+ if (this.renderer.minimap) {
+ this.minimap_circle.strokeColor = undefined;
+ }
+ this.super("unselect");
+ }
+};
+
+Rkns.Renderer.Node.prototype.highlight = function() {
+ if (this.highlighted) {
+ return;
+ }
+ this.highlighted = true;
+ this.redraw();
+ this.renderer.throttledPaperDraw();
+};
+
+Rkns.Renderer.Node.prototype.unhighlight = function() {
+ if (!this.highlighted) {
+ return;
+ }
+ this.highlighted = false;
+ this.redraw();
+ this.renderer.throttledPaperDraw();
+};
+
+Rkns.Renderer.Node.prototype.saveCoords = function() {
+ var _coords = this.renderer.toModelCoords(this.paper_coords),
+ _data = {
+ position: {
+ x: _coords.x,
+ y: _coords.y
+ }
+ };
+ if (this.renderer.isEditable()) {
+ this.model.set(_data);
+ }
+};
+
+Rkns.Renderer.Node.prototype.mousedown = function(_event, _isTouch) {
+ if (_isTouch) {
+ this.renderer.unselectAll();
+ this.select();
+ }
+};
+
+Rkns.Renderer.Node.prototype.mouseup = function(_event, _isTouch) {
+ if (this.renderer.is_dragging && this.renderer.isEditable()) {
+ this.saveCoords();
+ } else {
+ if (!_isTouch && !this.model.get("delete_scheduled")) {
+ this.openEditor();
+ }
+ this.model.trigger("clicked");
+ }
+ this.renderer.click_target = null;
+ this.renderer.is_dragging = false;
+ this.is_dragging = false;
+};
+
+Rkns.Renderer.Node.prototype.destroy = function(_event) {
+ this.super("destroy");
+ this.all_buttons.forEach(function(b) {
+ b.destroy();
+ });
+ this.circle.remove();
+ this.title.remove();
+ if (this.renderer.minimap) {
+ this.minimap_circle.remove();
+ }
+ if (this.node_image) {
+ this.node_image.remove();
+ }
+};
+
+/* */
+
+Rkns.Renderer.Edge = Rkns.Utils.inherit(Rkns.Renderer._BaseRepresentation);
+
+Rkns.Renderer.Edge.prototype._init = function() {
+ this.renderer.edge_layer.activate();
+ this.type = "Edge";
+ this.from_representation = this.renderer.getRepresentationByModel(this.model.get("from"));
+ this.to_representation = this.renderer.getRepresentationByModel(this.model.get("to"));
+ this.bundle = this.renderer.addToBundles(this);
+ this.line = new paper.Path();
+ this.line.add([0,0],[0,0],[0,0]);
+ this.line.__representation = this;
+ this.line.strokeWidth = this.options.edge_stroke_width;
+ this.arrow = new paper.Path();
+ this.arrow.add(
+ [ 0, 0 ],
+ [ this.options.edge_arrow_length, this.options.edge_arrow_width / 2 ],
+ [ 0, this.options.edge_arrow_width ]
+ );
+ this.arrow.__representation = this;
+ this.text = Rkns.$('<div class="Rk-Label Rk-Edge-Label">').appendTo(this.renderer.labels_$);
+ this.arrow_angle = 0;
+ if (this.options.editor_mode) {
+ this.normal_buttons = [
+ new Rkns.Renderer.EdgeEditButton(this.renderer, null),
+ new Rkns.Renderer.EdgeRemoveButton(this.renderer, null),
+ ];
+ this.pending_delete_buttons = [
+ new Rkns.Renderer.EdgeRevertButton(this.renderer, null)
+ ];
+ this.all_buttons = this.normal_buttons.concat(this.pending_delete_buttons);
+ for (var i = 0; i < this.all_buttons.length; i++) {
+ this.all_buttons[i].source_representation = this;
+ }
+ this.active_buttons = [];
+ } else {
+ this.active_buttons = this.all_buttons = [];
+ }
+
+ if (this.renderer.minimap) {
+ this.renderer.minimap.edge_layer.activate();
+ this.minimap_line = new paper.Path();
+ this.minimap_line.add([0,0],[0,0]);
+ this.minimap_line.__representation = this.renderer.minimap.miniframe.__representation;
+ this.minimap_line.strokeWidth = 1;
+ }
+};
+
+Rkns.Renderer.Edge.prototype.redraw = function() {
+ var from = this.model.get("from"),
+ to = this.model.get("to");
+ if (!from || !to) {
+ return;
+ }
+ this.from_representation = this.renderer.getRepresentationByModel(from);
+ this.to_representation = this.renderer.getRepresentationByModel(to);
+ if (typeof this.from_representation === "undefined" || typeof this.to_representation === "undefined") {
+ return;
+ }
+ var _p0a = this.from_representation.paper_coords,
+ _p1a = this.to_representation.paper_coords,
+ _v = _p1a.subtract(_p0a),
+ _r = _v.length,
+ _u = _v.divide(_r),
+ _ortho = new paper.Point([- _u.y, _u.x]),
+ _group_pos = this.bundle.getPosition(this),
+ _delta = _ortho.multiply( this.options.edge_gap_in_bundles * _group_pos ),
+ _p0b = _p0a.add(_delta), /* Adding a 4 px difference */
+ _p1b = _p1a.add(_delta), /* to differentiate bundled links */
+ _a = _v.angle,
+ _textdelta = _ortho.multiply(this.options.edge_label_distance),
+ _handle = _v.divide(3),
+ _color = this.model.get("color") || this.model.get("color") || (this.model.get("created_by") || Rkns.Renderer._USER_PLACEHOLDER(this.renkan)).get("color");
+
+ if (this.model.get("delete_scheduled") || this.from_representation.model.get("delete_scheduled") || this.to_representation.model.get("delete_scheduled")) {
+ var opacity = .5;
+ this.line.dashArray = [2, 2];
+ } else {
+ var opacity = 1;
+ this.line.dashArray = null;
+ }
+
+ var old_act_btn = this.active_buttons;
+
+ this.active_buttons = this.model.get("delete_scheduled") ? this.pending_delete_buttons : this.normal_buttons;
+
+ if (this.selected && this.renderer.isEditable() && old_act_btn !== this.active_buttons) {
+ old_act_btn.forEach(function(b) {
+ b.hide();
+ });
+ this.active_buttons.forEach(function(b) {
+ b.show();
+ });
+ }
+
+ this.paper_coords = _p0b.add(_p1b).divide(2);
+ this.line.strokeColor = _color;
+ this.line.opacity = opacity;
+ this.line.segments[0].point = _p0a;
+ this.line.segments[1].point = this.paper_coords;
+ this.line.segments[1].handleIn = _handle.multiply(-1);
+ this.line.segments[1].handleOut = _handle;
+ this.line.segments[2].point = _p1a;
+ this.arrow.rotate(_a - this.arrow_angle);
+ this.arrow.fillColor = _color;
+ this.arrow.opacity = opacity;
+ this.arrow.position = this.paper_coords;
+ this.arrow_angle = _a;
+ if (_a > 90) {
+ _a -= 180;
+ _textdelta = _textdelta.multiply(-1);
+ }
+ if (_a < -90) {
+ _a += 180;
+ _textdelta = _textdelta.multiply(-1);
+ }
+ var _text = this.model.get("title") || this.renkan.translate(this.options.label_untitled_edges) || "";
+ _text = Rkns.Renderer.shortenText(_text, this.options.node_label_max_length);
+ this.text.text(_text);
+ var _textpos = this.paper_coords.add(_textdelta);
+ this.text.css({
+ left: _textpos.x,
+ top: _textpos.y,
+ transform: "rotate(" + _a + "deg)",
+ "-moz-transform": "rotate(" + _a + "deg)",
+ "-webkit-transform": "rotate(" + _a + "deg)",
+ opacity: opacity
+ });
+ this.text_angle = _a;
+
+ var _pc = this.paper_coords;
+ this.all_buttons.forEach(function(b) {
+ b.moveTo(_pc);
+ });
+
+ if (this.renderer.minimap) {
+ this.minimap_line.strokeColor = _color;
+ this.minimap_line.segments[0].point = this.renderer.toMinimapCoords(new paper.Point(this.from_representation.model.get("position")));
+ this.minimap_line.segments[1].point = this.renderer.toMinimapCoords(new paper.Point(this.to_representation.model.get("position")));
+ }
+};
+
+Rkns.Renderer.Edge.prototype.openEditor = function() {
+ this.renderer.removeRepresentationsOfType("editor");
+ var _editor = this.renderer.addRepresentation("EdgeEditor",null);
+ _editor.source_representation = this;
+ _editor.draw();
+};
+
+Rkns.Renderer.Edge.prototype.select = function() {
+ this.selected = true;
+ this.line.strokeWidth = this.options.selected_edge_stroke_width;
+ if (this.renderer.isEditable()) {
+ this.active_buttons.forEach(function(b) {
+ b.show();
+ });
+ }
+ if (!this.options.editor_mode) {
+ this.openEditor();
+ }
+ this.super("select");
+};
+
+Rkns.Renderer.Edge.prototype.unselect = function(_newTarget) {
+ if (!_newTarget || _newTarget.source_representation !== this) {
+ this.selected = false;
+ if (this.options.editor_mode) {
+ this.all_buttons.forEach(function(b) {
+ b.hide();
+ });
+ }
+ this.line.strokeWidth = this.options.edge_stroke_width;
+ this.super("unselect");
+ }
+};
+
+Rkns.Renderer.Edge.prototype.mousedown = function(_event, _isTouch) {
+ if (_isTouch) {
+ this.renderer.unselectAll();
+ this.select();
+ }
+};
+
+Rkns.Renderer.Edge.prototype.mouseup = function(_event, _isTouch) {
+ if (!this.renkan.read_only && this.renderer.is_dragging) {
+ this.from_representation.saveCoords();
+ this.to_representation.saveCoords();
+ this.from_representation.is_dragging = false;
+ this.to_representation.is_dragging = false;
+ } else {
+ if (!_isTouch) {
+ this.openEditor();
+ }
+ this.model.trigger("clicked");
+ }
+ this.renderer.click_target = null;
+ this.renderer.is_dragging = false;
+};
+
+Rkns.Renderer.Edge.prototype.paperShift = function(_delta) {
+ if (this.options.editor_mode) {
+ if (!this.options.read_only) {
+ this.from_representation.paperShift(_delta);
+ this.to_representation.paperShift(_delta);
+ }
+ } else {
+ this.renderer.paperShift(_delta);
+ }
+};
+
+Rkns.Renderer.Edge.prototype.destroy = function() {
+ this.super("destroy");
+ this.line.remove();
+ this.arrow.remove();
+ this.text.remove();
+ if (this.renderer.minimap) {
+ this.minimap_line.remove();
+ }
+ this.all_buttons.forEach(function(b) {
+ b.destroy();
+ });
+ var _this = this;
+ this.bundle.edges = Rkns._(this.bundle.edges).reject(function(_edge) {
+ return _edge === _this;
+ });
+};
+
+/* */
+
+Rkns.Renderer.TempEdge = Rkns.Utils.inherit(Rkns.Renderer._BaseRepresentation);
+
+Rkns.Renderer.TempEdge.prototype._init = function() {
+ this.renderer.edge_layer.activate();
+ this.type = "Temp-edge";
+
+ var _color = (this.project.get("users").get(this.renkan.current_user) || Rkns.Renderer._USER_PLACEHOLDER(this.renkan)).get("color");
+ this.line = new paper.Path();
+ this.line.strokeColor = _color;
+ this.line.dashArray = [4, 2];
+ this.line.strokeWidth = this.options.selected_edge_stroke_width;
+ this.line.add([0,0],[0,0]);
+ this.line.__representation = this;
+ this.arrow = new paper.Path();
+ this.arrow.fillColor = _color;
+ this.arrow.add(
+ [ 0, 0 ],
+ [ this.options.edge_arrow_length, this.options.edge_arrow_width / 2 ],
+ [ 0, this.options.edge_arrow_width ]
+ );
+ this.arrow.__representation = this;
+ this.arrow_angle = 0;
+};
+
+Rkns.Renderer.TempEdge.prototype.redraw = function() {
+ var _p0 = this.from_representation.paper_coords,
+ _p1 = this.end_pos,
+ _a = _p1.subtract(_p0).angle,
+ _c = _p0.add(_p1).divide(2);
+ this.line.segments[0].point = _p0;
+ this.line.segments[1].point = _p1;
+ this.arrow.rotate(_a - this.arrow_angle);
+ this.arrow.position = _c;
+ this.arrow_angle = _a;
+};
+
+Rkns.Renderer.TempEdge.prototype.paperShift = function(_delta) {
+ if (!this.renderer.isEditable()) {
+ this.renderer.removeRepresentation(_this);
+ paper.view.draw();
+ return;
+ }
+ this.end_pos = this.end_pos.add(_delta);
+ var _hitResult = paper.project.hitTest(this.end_pos);
+ this.renderer.findTarget(_hitResult);
+ this.redraw();
+};
+
+Rkns.Renderer.TempEdge.prototype.mouseup = function(_event, _isTouch) {
+ var _hitResult = paper.project.hitTest(_event.point),
+ _model = this.from_representation.model,
+ _endDrag = true;
+ if (_hitResult && typeof _hitResult.item.__representation !== "undefined") {
+ var _target = _hitResult.item.__representation;
+ if (_target.type.substr(0,4) === "Node") {
+ var _destmodel = _target.model || _target.source_representation.model;
+ if (_model !== _destmodel) {
+ var _data = {
+ id: Rkns.Utils.getUID('edge'),
+ created_by: this.renkan.current_user,
+ from: _model,
+ to: _destmodel
+ };
+ if (this.renderer.isEditable()) {
+ this.project.addEdge(_data);
+ }
+ }
+ }
+
+ if (_model === _target.model || (_target.source_representation && _target.source_representation.model === _model)) {
+ _endDrag = false;
+ this.renderer.is_dragging = true;
+ }
+ }
+ if (_endDrag) {
+ this.renderer.click_target = null;
+ this.renderer.is_dragging = false;
+ this.renderer.removeRepresentation(this);
+ paper.view.draw();
+ }
+};
+
+Rkns.Renderer.TempEdge.prototype.destroy = function() {
+ this.arrow.remove();
+ this.line.remove();
+};
+
+/* */
+
+Rkns.Renderer._BaseEditor = Rkns.Utils.inherit(Rkns.Renderer._BaseRepresentation);
+
+Rkns.Renderer._BaseEditor.prototype._init = function() {
+ this.renderer.buttons_layer.activate();
+ this.type = "editor";
+ this.editor_block = new paper.Path();
+ var _pts = Rkns._(Rkns._.range(8)).map(function() {return [0,0];});
+ this.editor_block.add.apply(this.editor_block, _pts);
+ this.editor_block.strokeWidth = this.options.tooltip_border_width;
+ this.editor_block.strokeColor = this.options.tooltip_border_color;
+ this.editor_block.opacity = .8;
+ this.editor_$ = Rkns.$('<div>')
+ .appendTo(this.renderer.editor_$)
+ .css({
+ position: "absolute",
+ opacity: .8
+ })
+ .hide();
+};
+
+Rkns.Renderer._BaseEditor.prototype.destroy = function() {
+ this.editor_block.remove();
+ this.editor_$.remove();
+};
+
+/* */
+
+Rkns.Renderer.NodeEditor = Rkns.Utils.inherit(Rkns.Renderer._BaseEditor);
+
+Rkns.Renderer.NodeEditor.prototype.template = Rkns._.template(
+ '<h2><span class="Rk-CloseX">×</span><%-renkan.translate("Edit Node")%></span></h2>'
+ + '<p><label><%-renkan.translate("Title:")%></label><input class="Rk-Edit-Title" type="text" value="<%-node.title%>"/></p>'
+ + '<% if (options.show_node_editor_uri) { %><p><label><%-renkan.translate("URI:")%></label><input class="Rk-Edit-URI" type="text" value="<%-node.uri%>"/><a class="Rk-Edit-Goto" href="<%-node.uri%>" target="_blank"></a></p><% } %>'
+ + '<% if (options.show_node_editor_description) { %><p><label><%-renkan.translate("Description:")%></label><textarea class="Rk-Edit-Description"><%-node.description%></textarea></p><% } %>'
+ + '<% if (options.show_node_editor_size) { %><p><span class="Rk-Editor-Label"><%-renkan.translate("Size:")%></span><a href="#" class="Rk-Edit-Size-Down">-</a><span class="Rk-Edit-Size-Value"><%-node.size%></span><a href="#" class="Rk-Edit-Size-Up">+</a></p><% } %>'
+ + '<% if (options.show_node_editor_color) { %><div class="Rk-Editor-p"><span class="Rk-Editor-Label"><%-renkan.translate("Node color:")%></span><div class="Rk-Edit-ColorPicker-Wrapper"><span class="Rk-Edit-Color" style="background:<%-node.color%>;"><span class="Rk-Edit-ColorTip"></span></span><ul class="Rk-Edit-ColorPicker">'
+ + '<% _(Rkns.pickerColors).each(function(c) { %><li data-color="<%=c%>" style="background: <%=c%>"></li><% }); %></ul><span class="Rk-Edit-ColorPicker-Text"><%- renkan.translate("Choose color") %></span></div></div><% } %>'
+ + '<% if (options.show_node_editor_image) { %><div class="Rk-Edit-ImgWrap"><div class="Rk-Edit-ImgPreview"><img src="<%-node.image || node.image_placeholder%>" />'
+ + '<% if (node.clip_path) { %><svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewbox="0 0 1 1" preserveAspectRatio="none"><path style="stroke-width: .02; stroke:red; fill-opacity:.3; fill:red;" d="<%- node.clip_path %>"/></svg><% }%>'
+ + '</div></div><p><label><%-renkan.translate("Image URL:")%></label><input class="Rk-Edit-Image" type="text" value="<%-node.image%>"/></p>'
+ + '<p><label><%-renkan.translate("Choose Image File:")%></label><input class="Rk-Edit-Image-File" type="file" accept="image/*"/></p><% } %>'
+ + '<% if (options.show_node_editor_creator && node.has_creator) { %><p><span class="Rk-Editor-Label"><%-renkan.translate("Created by:")%></span> <span class="Rk-UserColor" style="background:<%-node.created_by_color%>;"></span><%- Rkns.Renderer.shortenText(node.created_by_title, 25) %></p><% } %>'
+);
+
+Rkns.Renderer.NodeEditor.prototype.readOnlyTemplate = Rkns._.template(
+ '<h2><span class="Rk-CloseX">×</span><% if (options.show_node_tooltip_color) { %><span class="Rk-UserColor" style="background:<%-node.color%>;"></span><% } %>'
+ + '<span class="Rk-Display-Title"><% if (node.uri) { %><a href="<%-node.uri%>" target="_blank"><% } %><%-node.title%><% if (node.uri) { %></a><% } %></span></h2>'
+ + '<% if (node.uri && options.show_node_tooltip_uri) { %><p class="Rk-Display-URI"><a href="<%-node.uri%>" target="_blank"><%-node.short_uri%></a></p><% } %>'
+ + '<% if (options.show_node_tooltip_description) { %><p><%-node.description%></p><% } %>'
+ + '<% if (node.image && options.show_node_tooltip_image) { %><img class="Rk-Display-ImgPreview" src="<%-node.image%>" /><% } %>'
+ + '<% if (node.has_creator && options.show_node_tooltip_creator) { %><p><span class="Rk-Editor-Label"><%-renkan.translate("Created by:")%></span><span class="Rk-UserColor" style="background:<%-node.created_by_color%>;"></span><%- Rkns.Renderer.shortenText(node.created_by_title, 25) %></p><% } %>'
+);
+
+Rkns.Renderer.NodeEditor.prototype.draw = function() {
+ var _model = this.source_representation.model,
+ _created_by = _model.get("created_by") || Rkns.Renderer._USER_PLACEHOLDER(this.renkan),
+ _template = (this.renderer.isEditable() ? this.template : this.readOnlyTemplate ),
+ _image_placeholder = this.options.static_url + "img/image-placeholder.png",
+ _size = (_model.get("size") || 0);
+ this.editor_$
+ .html(_template({
+ node: {
+ has_creator: !!_model.get("created_by"),
+ title: _model.get("title"),
+ uri: _model.get("uri"),
+ short_uri: Rkns.Renderer.shortenText((_model.get("uri") || "").replace(/^(https?:\/\/)?(www\.)?/,'').replace(/\/$/,''),40),
+ description: _model.get("description"),
+ image: _model.get("image") || "",
+ image_placeholder: _image_placeholder,
+ color: _model.get("color") || _created_by.get("color"),
+ clip_path: _model.get("clip-path") || false,
+ created_by_color: _created_by.get("color"),
+ created_by_title: _created_by.get("title"),
+ size: (_size > 0 ? "+" : "") + _size
+ },
+ renkan: this.renkan,
+ options: this.options
+ }));
+ this.redraw();
+ var _this = this,
+ closeEditor = function() {
+ _this.renderer.removeRepresentation(_this);
+ paper.view.draw();
+ };
+
+ this.editor_$.find(".Rk-CloseX").click(closeEditor);
+
+ if (this.renderer.isEditable()) {
+
+ var onFieldChange = Rkns._(function() {
+ Rkns._(function() {
+ if (_this.renderer.isEditable()) {
+ var _data = {
+ title: _this.editor_$.find(".Rk-Edit-Title").val()
+ };
+ if (_this.options.show_node_editor_uri) {
+ _data.uri = _this.editor_$.find(".Rk-Edit-URI").val();
+ }
+ if (_this.options.show_node_editor_image) {
+ _data.image = _this.editor_$.find(".Rk-Edit-Image").val();
+ _this.editor_$.find(".Rk-Edit-ImgPreview").attr("src", _data.image || _image_placeholder);
+ }
+ if (_this.options.show_node_editor_description) {
+ _data.description = _this.editor_$.find(".Rk-Edit-Description").val();
+ }
+ _model.set(_data);
+ _this.redraw();
+ } else {
+ closeEditor();
+ }
+
+ }).defer();
+ }).throttle(500);
+
+ this.editor_$.on("keyup", function(_e) {
+ if (_e.keyCode === 27) {
+ closeEditor();
+ }
+ });
+
+ this.editor_$.find("input, textarea").on("change keyup paste", onFieldChange);
+
+ this.editor_$.find(".Rk-Edit-Image-File").change(function() {
+ if (this.files.length) {
+ var f = this.files[0],
+ fr = new FileReader();
+ if (f.type.substr(0,5) !== "image") {
+ alert(_this.renkan.translate("This file is not an image"));
+ return;
+ }
+ if (f.size > (Rkns.Renderer._IMAGE_MAX_KB * 1024)) {
+ alert(_this.renkan.translate("Image size must be under ")+Rkns.Renderer._IMAGE_MAX_KB+_this.renkan.translate("KB"));
+ return;
+ }
+ fr.onload = function(e) {
+ _this.editor_$.find(".Rk-Edit-Image").val(e.target.result);
+ onFieldChange();
+ };
+ fr.readAsDataURL(f);
+ }
+ });
+ this.editor_$.find(".Rk-Edit-Title")[0].focus();
+
+ var _picker = _this.editor_$.find(".Rk-Edit-ColorPicker");
+
+ this.editor_$.find(".Rk-Edit-ColorPicker-Wrapper").hover(
+ function(_e) {
+ _e.preventDefault();
+ _picker.show();
+ },
+ function(_e) {
+ _e.preventDefault();
+ _picker.hide();
+ }
+ );
+
+ _picker.find("li").hover(
+ function(_e) {
+ _e.preventDefault();
+ _this.editor_$.find(".Rk-Edit-Color").css("background", $(this).attr("data-color"));
+ },
+ function(_e) {
+ _e.preventDefault();
+ _this.editor_$.find(".Rk-Edit-Color").css("background", _model.get("color") || (_model.get("created_by") || Rkns.Renderer._USER_PLACEHOLDER(_this.renkan)).get("color"));
+ }
+ ).click(function(_e) {
+ _e.preventDefault();
+ if (_this.renderer.isEditable()) {
+ _model.set("color", $(this).attr("data-color"));
+ _picker.hide();
+ paper.view.draw();
+ } else {
+ closeEditor();
+ }
+ });
+
+ function shiftSize(n) {
+ if (_this.renderer.isEditable()) {
+ var _newsize = n+(_model.get("size") || 0);
+ _this.editor_$.find(".Rk-Edit-Size-Value").text((_newsize > 0 ? "+" : "") + _newsize);
+ _model.set("size", _newsize);
+ paper.view.draw();
+ } else {
+ closeEditor();
+ }
+ }
+
+ this.editor_$.find(".Rk-Edit-Size-Down").click(function() {
+ shiftSize(-1);
+ return false;
+ });
+ this.editor_$.find(".Rk-Edit-Size-Up").click(function() {
+ shiftSize(1);
+ return false;
+ });
+ }
+ this.editor_$.find("img").load(function() {
+ _this.redraw();
+ });
+};
+
+Rkns.Renderer.NodeEditor.prototype.redraw = function() {
+ var _coords = this.source_representation.paper_coords;
+ Rkns.Renderer.drawEditBox(this.options, _coords, this.editor_block, this.source_representation.circle_radius * .75, this.editor_$);
+ this.editor_$.show();
+ paper.view.draw();
+};
+
+/* */
+
+Rkns.Renderer.EdgeEditor = Rkns.Utils.inherit(Rkns.Renderer._BaseEditor);
+
+Rkns.Renderer.EdgeEditor.prototype.template = Rkns._.template(
+ '<h2><span class="Rk-CloseX">×</span><%-renkan.translate("Edit Edge")%></span></h2>'
+ + '<p><label><%-renkan.translate("Title:")%></label><input class="Rk-Edit-Title" type="text" value="<%-edge.title%>"/></p>'
+ + '<% if (options.show_edge_editor_uri) { %><p><label><%-renkan.translate("URI:")%></label><input class="Rk-Edit-URI" type="text" value="<%-edge.uri%>"/><a class="Rk-Edit-Goto" href="<%-edge.uri%>" target="_blank"></a></p>'
+ + '<% if (options.properties.length) { %><p><label><%-renkan.translate("Choose from vocabulary:")%></label><select class="Rk-Edit-Vocabulary">'
+ + '<% _(options.properties).each(function(ontology) { %><option class="Rk-Edit-Vocabulary-Class" value=""><%- renkan.translate(ontology.label) %></option>'
+ + '<% _(ontology.properties).each(function(property) { var uri = ontology["base-uri"] + property.uri; %><option class="Rk-Edit-Vocabulary-Property" value="<%- uri %>'
+ + '"<% if (uri === edge.uri) { %> selected<% } %>><%- renkan.translate(property.label) %></option>'
+ + '<% }) %><% }) %></select></p><% } } %>'
+ + '<% if (options.show_edge_editor_color) { %><div class="Rk-Editor-p"><span class="Rk-Editor-Label"><%-renkan.translate("Edge color:")%></span><div class="Rk-Edit-ColorPicker-Wrapper"><span class="Rk-Edit-Color" style="background:<%-edge.color%>;"><span class="Rk-Edit-ColorTip"></span></span><ul class="Rk-Edit-ColorPicker">'
+ + '<% _(Rkns.pickerColors).each(function(c) { %><li data-color="<%=c%>" style="background: <%=c%>"></li><% }); %></ul><span class="Rk-Edit-ColorPicker-Text"><%- renkan.translate("Choose color") %></span></div></div><% } %>'
+ + '<% if (options.show_edge_editor_direction) { %><p><span class="Rk-Edit-Direction"><%- renkan.translate("Change edge direction") %></span></p><% } %>'
+ + '<% if (options.show_edge_editor_nodes) { %><p><span class="Rk-Editor-Label"><%-renkan.translate("From:")%></span><span class="Rk-UserColor" style="background:<%-edge.from_color%>;"></span><%- Rkns.Renderer.shortenText(edge.from_title, 25) %></p>'
+ + '<p><span class="Rk-Editor-Label"><%-renkan.translate("To:")%></span><span class="Rk-UserColor" style="background:<%-edge.to_color%>;"></span><%- Rkns.Renderer.shortenText(edge.to_title, 25) %></p><% } %>'
+ + '<% if (options.show_edge_editor_creator && edge.has_creator) { %><p><span class="Rk-Editor-Label"><%-renkan.translate("Created by:")%></span><span class="Rk-UserColor" style="background:<%-edge.created_by_color%>;"></span><%- Rkns.Renderer.shortenText(edge.created_by_title, 25) %></p><% } %>'
+);
+
+Rkns.Renderer.EdgeEditor.prototype.readOnlyTemplate = Rkns._.template(
+ '<h2><span class="Rk-CloseX">×</span><% if (options.show_edge_tooltip_color) { %><span class="Rk-UserColor" style="background:<%-edge.color%>;"></span><% } %>'
+ + '<span class="Rk-Display-Title"><% if (edge.uri) { %><a href="<%-edge.uri%>" target="_blank"><% } %><%-edge.title%><% if (edge.uri) { %></a><% } %></span></h2>'
+ + '<% if (options.show_edge_tooltip_uri && edge.uri) { %><p class="Rk-Display-URI"><a href="<%-edge.uri%>" target="_blank"><%-edge.short_uri%></a></p><% } %>'
+ + '<p><%-edge.description%></p>'
+ + '<% if (options.show_edge_tooltip_nodes) { %><p><span class="Rk-Editor-Label"><%-renkan.translate("From:")%></span><span class="Rk-UserColor" style="background:<%-edge.from_color%>;"></span><%- Rkns.Renderer.shortenText(edge.from_title, 25) %></p>'
+ + '<p><span class="Rk-Editor-Label"><%-renkan.translate("To:")%></span><span class="Rk-UserColor" style="background:<%-edge.to_color%>;"></span><%- Rkns.Renderer.shortenText(edge.to_title, 25) %></p><% } %>'
+ + '<% if (options.show_edge_tooltip_creator && edge.has_creator) { %><p><span class="Rk-Editor-Label"><%-renkan.translate("Created by:")%></span><span class="Rk-UserColor" style="background:<%-edge.created_by_color%>;"></span><%- Rkns.Renderer.shortenText(edge.created_by_title, 25) %></p><% } %>'
+);
+
+Rkns.Renderer.EdgeEditor.prototype.draw = function() {
+ var _model = this.source_representation.model,
+ _from_model = _model.get("from"),
+ _to_model = _model.get("to"),
+ _created_by = _model.get("created_by") || Rkns.Renderer._USER_PLACEHOLDER(this.renkan),
+ _template = (this.renderer.isEditable() ? this.template : this.readOnlyTemplate);
+ this.editor_$
+ .html(_template({
+ edge: {
+ has_creator: !!_model.get("created_by"),
+ title: _model.get("title"),
+ uri: _model.get("uri"),
+ short_uri: Rkns.Renderer.shortenText((_model.get("uri") || "").replace(/^(https?:\/\/)?(www\.)?/,'').replace(/\/$/,''),40),
+ description: _model.get("description"),
+ color: _model.get("color") || _created_by.get("color"),
+ from_title: _from_model.get("title"),
+ to_title: _to_model.get("title"),
+ from_color: _from_model.get("color") || (_from_model.get("created_by") || Rkns.Renderer._USER_PLACEHOLDER(this.renkan)).get("color"),
+ to_color: _to_model.get("color") || (_to_model.get("created_by") || Rkns.Renderer._USER_PLACEHOLDER(this.renkan)).get("color"),
+ created_by_color: _created_by.get("color"),
+ created_by_title: _created_by.get("title")
+ },
+ renkan: this.renkan,
+ options: this.options,
+ }));
+ this.redraw();
+ var _this = this,
+ closeEditor = function() {
+ _this.renderer.removeRepresentation(_this);
+ paper.view.draw();
+ };
+ this.editor_$.find(".Rk-CloseX").click(closeEditor);
+
+ if (this.renderer.isEditable()) {
+
+ var onFieldChange = Rkns._(function() {
+ Rkns._(function() {
+ if (_this.renderer.isEditable()) {
+ var _data = {
+ title: _this.editor_$.find(".Rk-Edit-Title").val()
+ };
+ if (_this.options.show_edge_editor_uri) {
+ _data.uri = _this.editor_$.find(".Rk-Edit-URI").val();
+ }
+ _this.editor_$.find(".Rk-Edit-Goto").attr("href",_data.uri);
+ _model.set(_data);
+ paper.view.draw();
+ } else {
+ closeEditor();
+ }
+ }).defer();
+ }).throttle(500);
+
+ this.editor_$.on("keyup", function(_e) {
+ if (_e.keyCode === 27) {
+ closeEditor();
+ }
+ });
+
+ this.editor_$.find("input").on("keyup change paste", onFieldChange);
+
+ this.editor_$.find(".Rk-Edit-Vocabulary").change(function() {
+ var e = $(this),
+ v = e.val();
+ if (v) {
+ _this.editor_$.find(".Rk-Edit-Title").val(e.find(":selected").text());
+ _this.editor_$.find(".Rk-Edit-URI").val(v);
+ onFieldChange();
+ }
+ });
+ this.editor_$.find(".Rk-Edit-Direction").click(function() {
+ if (_this.renderer.isEditable()) {
+ _model.set({
+ from: _model.get("to"),
+ to: _model.get("from")
+ });
+ _this.draw();
+ } else {
+ closeEditor();
+ }
+ });
+
+ var _picker = _this.editor_$.find(".Rk-Edit-ColorPicker");
+
+ this.editor_$.find(".Rk-Edit-ColorPicker-Wrapper").hover(
+ function(_e) {
+ _e.preventDefault();
+ _picker.show();
+ },
+ function(_e) {
+ _e.preventDefault();
+ _picker.hide();
+ }
+ );
+
+ _picker.find("li").hover(
+ function(_e) {
+ _e.preventDefault();
+ _this.editor_$.find(".Rk-Edit-Color").css("background", $(this).attr("data-color"));
+ },
+ function(_e) {
+ _e.preventDefault();
+ _this.editor_$.find(".Rk-Edit-Color").css("background", _model.get("color") || (_model.get("created_by") || Rkns.Renderer._USER_PLACEHOLDER(_this.renkan)).get("color"));
+ }
+ ).click(function(_e) {
+ _e.preventDefault();
+ if (_this.renderer.isEditable()) {
+ _model.set("color", $(this).attr("data-color"));
+ _picker.hide();
+ paper.view.draw();
+ } else {
+ closeEditor();
+ }
+ });
+ }
+};
+
+Rkns.Renderer.EdgeEditor.prototype.redraw = function() {
+ var _coords = this.source_representation.paper_coords;
+ Rkns.Renderer.drawEditBox(this.options, _coords, this.editor_block, 5, this.editor_$);
+ this.editor_$.show();
+ paper.view.draw();
+};
+
+/* */
+
+Rkns.Renderer._NodeButton = Rkns.Utils.inherit(Rkns.Renderer._BaseButton);
+
+Rkns.Renderer._NodeButton.prototype.setSectorSize = function() {
+ var sectorInner = this.source_representation.circle_radius;
+ if (sectorInner !== this.lastSectorInner) {
+ if (this.sector) {
+ this.sector.destroy();
+ }
+ this.sector = this.renderer.drawSector(
+ this, 1 + sectorInner,
+ Rkns.Renderer._NODE_BUTTON_WIDTH + sectorInner,
+ this.startAngle,
+ this.endAngle,
+ 1,
+ this.imageName,
+ this.renkan.translate(this.text)
+ );
+ this.lastSectorInner = sectorInner;
+ }
+};
+
+/* */
+
+Rkns.Renderer.NodeEditButton = Rkns.Utils.inherit(Rkns.Renderer._NodeButton);
+
+Rkns.Renderer.NodeEditButton.prototype._init = function() {
+ this.type = "Node-edit-button";
+ this.lastSectorInner = 0;
+ this.startAngle = -135;
+ this.endAngle = -45;
+ this.imageName = "edit";
+ this.text = "Edit";
+};
+
+Rkns.Renderer.NodeEditButton.prototype.mouseup = function() {
+ if (!this.renderer.is_dragging) {
+ this.source_representation.openEditor();
+ }
+};
+
+/* */
+
+Rkns.Renderer.NodeRemoveButton = Rkns.Utils.inherit(Rkns.Renderer._NodeButton);
+
+Rkns.Renderer.NodeRemoveButton.prototype._init = function() {
+ this.type = "Node-remove-button";
+ this.lastSectorInner = 0;
+ this.startAngle = 0;
+ this.endAngle = 90;
+ this.imageName = "remove";
+ this.text = "Remove";
+};
+
+Rkns.Renderer.NodeRemoveButton.prototype.mouseup = function() {
+ this.renderer.click_target = null;
+ this.renderer.is_dragging = false;
+ this.renderer.removeRepresentationsOfType("editor");
+ if (this.renderer.isEditable()) {
+ if (this.options.element_delete_delay) {
+ var delid = Rkns.Utils.getUID("delete");
+ this.renderer.delete_list.push({
+ id: delid,
+ time: new Date().valueOf() + this.options.element_delete_delay
+ });
+ this.source_representation.model.set("delete_scheduled", delid);
+ } else {
+ if (confirm(this.renkan.translate('Do you really wish to remove node ') + '"' + this.source_representation.model.get("title") + '"?')) {
+ this.project.removeNode(this.source_representation.model);
+ }
+ }
+ }
+};
+
+/* */
+
+Rkns.Renderer.NodeRevertButton = Rkns.Utils.inherit(Rkns.Renderer._NodeButton);
+
+Rkns.Renderer.NodeRevertButton.prototype._init = function() {
+ this.type = "Node-revert-button";
+ this.lastSectorInner = 0;
+ this.startAngle = -135;
+ this.endAngle = 135;
+ this.imageName = "revert";
+ this.text = "Cancel deletion";
+};
+
+Rkns.Renderer.NodeRevertButton.prototype.mouseup = function() {
+ this.renderer.click_target = null;
+ this.renderer.is_dragging = false;
+ if (this.renderer.isEditable()) {
+ this.source_representation.model.unset("delete_scheduled");
+ }
+};
+
+/* */
+
+Rkns.Renderer.NodeLinkButton = Rkns.Utils.inherit(Rkns.Renderer._NodeButton);
+
+Rkns.Renderer.NodeLinkButton.prototype._init = function() {
+ this.type = "Node-link-button";
+ this.lastSectorInner = 0;
+ this.startAngle = 90;
+ this.endAngle = 180;
+ this.imageName = "link";
+ this.text = "Link to another node";
+};
+
+Rkns.Renderer.NodeLinkButton.prototype.mousedown = function(_event, _isTouch) {
+ if (this.renderer.isEditable()) {
+ var _off = this.renderer.canvas_$.offset(),
+ _point = new paper.Point([
+ _event.pageX - _off.left,
+ _event.pageY - _off.top
+ ]);
+ this.renderer.click_target = null;
+ this.renderer.removeRepresentationsOfType("editor");
+ this.renderer.addTempEdge(this.source_representation, _point);
+ }
+};
+
+/* */
+
+Rkns.Renderer.NodeEnlargeButton = Rkns.Utils.inherit(Rkns.Renderer._NodeButton);
+
+Rkns.Renderer.NodeEnlargeButton.prototype._init = function() {
+ this.type = "Node-enlarge-button";
+ this.lastSectorInner = 0;
+ this.startAngle = -45;
+ this.endAngle = 0;
+ this.imageName = "enlarge";
+ this.text = "Enlarge";
+};
+
+Rkns.Renderer.NodeEnlargeButton.prototype.mouseup = function() {
+ var _newsize = 1 + (this.source_representation.model.get("size") || 0);
+ this.source_representation.model.set("size", _newsize);
+ this.source_representation.select();
+ this.select();
+ paper.view.draw();
+};
+
+/* */
+
+Rkns.Renderer.NodeShrinkButton = Rkns.Utils.inherit(Rkns.Renderer._NodeButton);
+
+Rkns.Renderer.NodeShrinkButton.prototype._init = function() {
+ this.type = "Node-shrink-button";
+ this.lastSectorInner = 0;
+ this.startAngle = -180;
+ this.endAngle = -135;
+ this.imageName = "shrink";
+ this.text = "Shrink";
+};
+
+Rkns.Renderer.NodeShrinkButton.prototype.mouseup = function() {
+ var _newsize = -1 + (this.source_representation.model.get("size") || 0);
+ this.source_representation.model.set("size", _newsize);
+ this.source_representation.select();
+ this.select();
+ paper.view.draw();
+};
+
+/* */
+
+Rkns.Renderer.EdgeEditButton = Rkns.Utils.inherit(Rkns.Renderer._BaseButton);
+
+Rkns.Renderer.EdgeEditButton.prototype._init = function() {
+ this.type = "Edge-edit-button";
+ this.sector = this.renderer.drawSector(this, Rkns.Renderer._EDGE_BUTTON_INNER, Rkns.Renderer._EDGE_BUTTON_OUTER, -270, -90, 1, "edit", this.renkan.translate("Edit"));
+};
+
+Rkns.Renderer.EdgeEditButton.prototype.mouseup = function() {
+ if (!this.renderer.is_dragging) {
+ this.source_representation.openEditor();
+ }
+};
+
+/* */
+
+Rkns.Renderer.EdgeRemoveButton = Rkns.Utils.inherit(Rkns.Renderer._BaseButton);
+
+Rkns.Renderer.EdgeRemoveButton.prototype._init = function() {
+ this.type = "Edge-remove-button";
+ this.sector = this.renderer.drawSector(this, Rkns.Renderer._EDGE_BUTTON_INNER, Rkns.Renderer._EDGE_BUTTON_OUTER, -90, 90, 1, "remove", this.renkan.translate("Remove"));
+};
+
+Rkns.Renderer.EdgeRemoveButton.prototype.mouseup = function() {
+ this.renderer.click_target = null;
+ this.renderer.is_dragging = false;
+ this.renderer.removeRepresentationsOfType("editor");
+ if (this.renderer.isEditable()) {
+ if (this.options.element_delete_delay) {
+ var delid = Rkns.Utils.getUID("delete");
+ this.renderer.delete_list.push({
+ id: delid,
+ time: new Date().valueOf() + this.options.element_delete_delay
+ });
+ this.source_representation.model.set("delete_scheduled", delid);
+ } else {
+ if (confirm(this.renkan.translate('Do you really wish to remove edge ') + '"' + this.source_representation.model.get("title") + '"?')) {
+ this.project.removeEdge(this.source_representation.model);
+ }
+ }
+ }
+};
+
+/* */
+
+Rkns.Renderer.EdgeRevertButton = Rkns.Utils.inherit(Rkns.Renderer._BaseButton);
+
+Rkns.Renderer.EdgeRevertButton.prototype._init = function() {
+ this.type = "Edge-revert-button";
+ this.sector = this.renderer.drawSector(this, Rkns.Renderer._EDGE_BUTTON_INNER, Rkns.Renderer._EDGE_BUTTON_OUTER, -135, 135, 1, "revert", this.renkan.translate("Cancel deletion"));
+};
+
+Rkns.Renderer.EdgeRevertButton.prototype.mouseup = function() {
+ this.renderer.click_target = null;
+ this.renderer.is_dragging = false;
+ if (this.renderer.isEditable()) {
+ this.source_representation.model.unset("delete_scheduled");
+ }
+};
+
+/* */
+
+Rkns.Renderer.MiniFrame = Rkns.Utils.inherit(Rkns.Renderer._BaseRepresentation);
+
+Rkns.Renderer.MiniFrame.prototype.paperShift = function(_delta) {
+ this.renderer.offset = this.renderer.offset.subtract(_delta.divide(this.renderer.minimap.scale).multiply(this.renderer.scale));
+ this.renderer.redraw();
+};
+
+Rkns.Renderer.MiniFrame.prototype.mouseup = function(_delta) {
+ this.renderer.click_target = null;
+ this.renderer.is_dragging = false;
+};
+
+/* */
+
+Rkns.Renderer.Scene = function(_renkan) {
+ this.renkan = _renkan;
+ this.$ = Rkns.$(".Rk-Render");
+ this.representations = [];
+ this.$.html(this.template(_renkan));
+ this.onStatusChange();
+ this.canvas_$ = this.$.find(".Rk-Canvas");
+ this.labels_$ = this.$.find(".Rk-Labels");
+ this.editor_$ = this.$.find(".Rk-Editor");
+ this.notif_$ = this.$.find(".Rk-Notifications");
+ paper.setup(this.canvas_$[0]);
+ this.scale = 1;
+ this.offset = paper.view.center;
+ this.totalScroll = 0;
+ this.mouse_down = false;
+ this.click_target = null;
+ this.selected_target = null;
+ this.edge_layer = new paper.Layer();
+ this.node_layer = new paper.Layer();
+ this.buttons_layer = new paper.Layer();
+ this.delete_list = [];
+
+ if (_renkan.options.show_minimap) {
+ this.minimap = {
+ background_layer: new paper.Layer(),
+ edge_layer: new paper.Layer(),
+ node_layer: new paper.Layer(),
+ node_group: new paper.Group(),
+ size: new paper.Size( _renkan.options.minimap_width, _renkan.options.minimap_height )
+ };
+
+ this.minimap.background_layer.activate();
+ this.minimap.topleft = paper.view.bounds.bottomRight.subtract(this.minimap.size);
+ this.minimap.rectangle = new paper.Path.Rectangle(this.minimap.topleft.subtract([2,2]), this.minimap.size.add([4,4]));
+ this.minimap.rectangle.fillColor = _renkan.options.minimap_background_color;
+ this.minimap.rectangle.strokeColor = _renkan.options.minimap_border_color;
+ this.minimap.rectangle.strokeWidth = 4;
+ this.minimap.offset = new paper.Point(this.minimap.size.divide(2));
+ this.minimap.scale = .1;
+
+ this.minimap.node_layer.activate();
+ this.minimap.cliprectangle = new paper.Path.Rectangle(this.minimap.topleft, this.minimap.size);
+ this.minimap.node_group.addChild(this.minimap.cliprectangle);
+ this.minimap.node_group.clipped = true;
+ this.minimap.miniframe = new paper.Path.Rectangle(this.minimap.topleft, this.minimap.size);
+ this.minimap.node_group.addChild(this.minimap.miniframe);
+ this.minimap.miniframe.fillColor = '#c0c0ff';
+ this.minimap.miniframe.opacity = .3;
+ this.minimap.miniframe.strokeColor = '#000080';
+ this.minimap.miniframe.strokeWidth = 3;
+ this.minimap.miniframe.__representation = new Rkns.Renderer.MiniFrame(this, null);
+ }
+
+ this.throttledPaperDraw = Rkns._(function() {
+ paper.view.draw();
+ }).throttle(100);
+
+ this.bundles = [];
+ this.click_mode = false;
+
+ var _this = this,
+ _allowScroll = true,
+ _originalScale,
+ _zooming = false,
+ _lastTapDate,
+ _lastTapX,
+ _lastTapY;
+
+ this.image_cache = {};
+ this.icon_cache = {};
+
+ ['edit', 'remove', 'link', 'enlarge', 'shrink', 'revert' ].forEach(function(imgname) {
+ var img = new Image();
+ img.src = _renkan.options.static_url + 'img/' + imgname + '.png';
+ _this.icon_cache[imgname] = img;
+ });
+
+ var throttledMouseMove = _.throttle(function(_event, _isTouch) {
+ _this.onMouseMove(_event, _isTouch);
+ }, Rkns.Renderer._MOUSEMOVE_RATE);
+
+ this.canvas_$.on({
+ mousedown: function(_event) {
+ _event.preventDefault();
+ _this.onMouseDown(_event, false);
+ },
+ mousemove: function(_event) {
+ _event.preventDefault();
+ throttledMouseMove(_event, false);
+ },
+ mouseup: function(_event) {
+ _event.preventDefault();
+ _this.onMouseUp(_event, false);
+ },
+ mousewheel: function(_event, _delta) {
+ _event.preventDefault();
+ if (_allowScroll) {
+ _this.onScroll(_event, _delta);
+ }
+ },
+ touchstart: function(_event) {
+ _event.preventDefault();
+ var _touches = _event.originalEvent.touches[0];
+ if (
+ _renkan.options.allow_double_click
+ && new Date() - _lastTap < Rkns.Renderer._DOUBLETAP_DELAY
+ && ( Math.pow(_lastTapX - _touches.pageX, 2) + Math.pow(_lastTapY - _touches.pageY, 2) < Rkns.Renderer._DOUBLETAP_DISTANCE )
+ ) {
+ _lastTap = 0;
+ _this.onDoubleClick(_touches);
+ } else {
+ _lastTap = new Date();
+ _lastTapX = _touches.pageX;
+ _lastTapY = _touches.pageY;
+ _originalScale = _this.scale;
+ _zooming = false;
+ _this.onMouseDown(_touches, true);
+ }
+ },
+ touchmove: function(_event) {
+ _event.preventDefault();
+ _lastTap = 0;
+ if (_event.originalEvent.touches.length == 1) {
+ _this.onMouseMove(_event.originalEvent.touches[0], true);
+ } else {
+ if (!_zooming) {
+ _this.onMouseUp(_event.originalEvent.touches[0], true);
+ _this.click_target = null;
+ _this.is_dragging = false;
+ _zooming = true;
+ }
+ if (_event.originalEvent.scale === "undefined") {
+ return;
+ }
+ var _newScale = _event.originalEvent.scale * _originalScale,
+ _scaleRatio = _newScale / _this.scale,
+ _newOffset = new paper.Point([
+ _this.canvas_$.width(),
+ _this.canvas_$.height()
+ ]).multiply( .5 * ( 1 - _scaleRatio ) ).add(_this.offset.multiply( _scaleRatio ));
+ _this.setScale(_newScale, _this.offset);
+ }
+ },
+ touchend: function(_event) {
+ _event.preventDefault();
+ _this.onMouseUp(_event.originalEvent.changedTouches[0], true);
+ },
+ dblclick: function(_event) {
+ _event.preventDefault();
+ if (_renkan.options.allow_double_click) {
+ _this.onDoubleClick(_event);
+ }
+ },
+ mouseleave: function(_event) {
+ _event.preventDefault();
+ _this.onMouseUp(_event, false);
+ _this.click_target = null;
+ _this.is_dragging = false;
+ },
+ dragover: function(_event) {
+ _event.preventDefault();
+ },
+ dragenter: function(_event) {
+ _event.preventDefault();
+ _allowScroll = false;
+ },
+ dragleave: function(_event) {
+ _event.preventDefault();
+ _allowScroll = true;
+ },
+ drop: function(_event) {
+ _event.preventDefault();
+ _allowScroll = true;
+ var res = {};
+ Rkns._(_event.originalEvent.dataTransfer.types).each(function(t) {
+ try {
+ res[t] = _event.originalEvent.dataTransfer.getData(t);
+ } catch(e) {}
+ });
+ var text = _event.originalEvent.dataTransfer.getData("Text");
+ if (typeof text === "string") {
+ switch(text[0]) {
+ case "{":
+ case "[":
+ try {
+ var data = JSON.parse(text);
+ _(res).extend(data);
+ }
+ catch(e) {
+ if (!res["text/plain"]) {
+ res["text/plain"] = text;
+ }
+ }
+ break;
+ case "<":
+ if (!res["text/html"]) {
+ res["text/html"] = text;
+ }
+ break;
+ default:
+ if (!res["text/plain"]) {
+ res["text/plain"] = text;
+ }
+ }
+ }
+ var url = _event.originalEvent.dataTransfer.getData("URL");
+ if (url && !res["text/uri-list"]) {
+ res["text/uri-list"] = url;
+ }
+ _this.dropData(res, _event.originalEvent);
+ }
+ });
+ this.editor_$.find(".Rk-ZoomOut").click(function() {
+ var _newScale = _this.scale * Math.SQRT1_2,
+ _offset = new paper.Point([
+ _this.canvas_$.width(),
+ _this.canvas_$.height()
+ ]).multiply( .5 * ( 1 - Math.SQRT1_2 ) ).add(_this.offset.multiply( Math.SQRT1_2 ));
+ _this.setScale( _newScale, _offset );
+ });
+ this.editor_$.find(".Rk-ZoomIn").click(function() {
+ var _newScale = _this.scale * Math.SQRT2,
+ _offset = new paper.Point([
+ _this.canvas_$.width(),
+ _this.canvas_$.height()
+ ]).multiply( .5 * ( 1 - Math.SQRT2 ) ).add(_this.offset.multiply( Math.SQRT2 ));
+ _this.setScale( _newScale, _offset );
+ });
+ this.$.find(".Rk-CurrentUser").mouseenter(
+ function() { _this.$.find(".Rk-UserList").slideDown(); }
+ );
+ this.$.find(".Rk-Users").mouseleave(
+ function() { _this.$.find(".Rk-UserList").slideUp(); }
+ );
+ this.$.find(".Rk-FullScreen-Button").click(function() {
+ var _isFull = document.fullScreen || document.mozFullScreen || document.webkitIsFullScreen,
+ _el = _this.renkan.$[0],
+ _requestMethods = ["requestFullScreen","mozRequestFullScreen","webkitRequestFullScreen"],
+ _cancelMethods = ["cancelFullScreen","mozCancelFullScreen","webkitCancelFullScreen"];
+ if (_isFull) {
+ for (var i = 0; i < _cancelMethods.length; i++) {
+ if (typeof document[_cancelMethods[i]] === "function") {
+ document[_cancelMethods[i]]();
+ break;
+ }
+ }
+ } else {
+ for (var i = 0; i < _requestMethods.length; i++) {
+ if (typeof _el[_requestMethods[i]] === "function") {
+ _el[_requestMethods[i]]();
+ break;
+ }
+ }
+ }
+ });
+ this.$.find(".Rk-AddNode-Button").click(function() {
+ if (_this.click_mode === Rkns.Renderer._CLICKMODE_ADDNODE) {
+ _this.click_mode = false;
+ _this.notif_$.hide();
+ } else {
+ _this.click_mode = Rkns.Renderer._CLICKMODE_ADDNODE;
+ _this.notif_$.text(_renkan.translate("Click on the background canvas to add a node")).fadeIn();
+ }
+ });
+ this.$.find(".Rk-AddEdge-Button").click(function() {
+ if (_this.click_mode === Rkns.Renderer._CLICKMODE_STARTEDGE || _this.click_mode === Rkns.Renderer._CLICKMODE_ENDEDGE) {
+ _this.click_mode = false;
+ _this.notif_$.hide();
+ } else {
+ _this.click_mode = Rkns.Renderer._CLICKMODE_STARTEDGE;
+ _this.notif_$.text(_renkan.translate("Click on a first node to start the edge")).fadeIn();
+ }
+ });
+ this.$.find(".Rk-Bookmarklet-Button")
+ .attr("href","javascript:" + Rkns.Renderer._BOOKMARKLET_CODE(_renkan))
+ .click(function(){
+ _this.notif_$
+ .text(_renkan.translate("Drag this button to your bookmark bar. When on a third-party website, click it to enable drag-and-drop from the website to Renkan."))
+ .fadeIn()
+ .delay(5000)
+ .fadeOut();
+ return false;
+ });
+ this.$.find(".Rk-TopBar-Button").mouseover(function() {
+ Rkns.$(this).find(".Rk-TopBar-Tooltip").show();
+ }).mouseout(function() {
+ Rkns.$(this).find(".Rk-TopBar-Tooltip").hide();
+ });
+ this.$.find(".Rk-Fold-Bins").click(function() {
+ var bins = _renkan.$.find(".Rk-Bins");
+ if (bins.offset().left < 0) {
+ bins.animate({left: 0},250);
+ _this.$.animate({left: 300},250,function() {
+ var w = _this.$.width();
+ paper.view.viewSize = new paper.Size([w, _this.canvas_$.height()]);
+ });
+ $(this).html("«");
+ } else {
+ bins.animate({left: -300},250);
+ _this.$.animate({left: 0},250,function() {
+ var w = _this.$.width();
+ paper.view.viewSize = new paper.Size([w, _this.canvas_$.height()]);
+ });
+ $(this).html("»");
+ }
+ });
+
+ paper.view.onResize = function(_event) {
+ _this.offset = _this.offset.add(_event.delta.divide(2));
+ if (_this.minimap) {
+ _this.minimap.topleft = paper.view.bounds.bottomRight.subtract(_this.minimap.size);
+ _this.minimap.rectangle.fitBounds(_this.minimap.topleft.subtract([2,2]), _this.minimap.size.add([4,4]));
+ _this.minimap.cliprectangle.fitBounds(_this.minimap.topleft, _this.minimap.size);
+ }
+ _this.redraw();
+ };
+
+ var _thRedraw = Rkns._.throttle(function() {
+ _this.redraw();
+ },50);
+
+ this.addRepresentations("Node", this.renkan.project.get("nodes"));
+ this.addRepresentations("Edge", this.renkan.project.get("edges"));
+ this.renkan.project.on("change:title", function() {
+ _this.$.find(".Rk-PadTitle").val(_renkan.project.get("title"));
+ });
+
+ this.$.find(".Rk-PadTitle").on("keyup input paste", function() {
+ _renkan.project.set({"title": $(this).val()});
+ });
+
+ this.renkan.project.get("users").each(function(_user) {
+ _this.addUser(_user);
+ });
+
+ this.renkan.project.on("add:users", function(_user) {
+ _this.addUser(_user);
+ });
+ this.renkan.project.on("add:nodes", function(_node) {
+ _this.addRepresentation("Node", _node);
+ _thRedraw();
+ });
+ this.renkan.project.on("add:edges", function(_edge) {
+ _this.addRepresentation("Edge", _edge);
+ _thRedraw();
+ });
+ this.renkan.project.on("change:title", function(_model, _title) {
+ var el = _this.$.find(".Rk-PadTitle");
+ if (el.is("input")) {
+ if (el.val() !== _title) {
+ el.val(_title);
+ }
+ } else {
+ el.text(_title);
+ }
+ });
+
+ if (_renkan.options.size_bug_fix) {
+ var _delay = (
+ typeof _renkan.options.size_bug_fix === "number"
+ ? _renkan.options.size_bug_fix
+ : 500
+ );
+ window.setTimeout(
+ function() {
+ _this.fixSize(true);
+ },
+ _delay
+ );
+ }
+
+ if (_renkan.options.force_resize) {
+ $(window).resize(function() {
+ _this.fixSize(false);
+ });
+ }
+
+ this.redraw();
+
+ window.setInterval(function() {
+ var _now = new Date().valueOf();
+ _this.delete_list.forEach(function(d) {
+ if (_now >= d.time) {
+ var el = _renkan.project.get("nodes").findWhere({"delete_scheduled":d.id});
+ if (el) {
+ project.removeNode(el);
+ }
+ el = _renkan.project.get("edges").findWhere({"delete_scheduled":d.id});
+ if (el) {
+ project.removeEdge(el);
+ }
+ }
+ });
+ _this.delete_list = _this.delete_list.filter(function(d) {
+ return _renkan.project.get("nodes").findWhere({"delete_scheduled":d.id}) || _renkan.project.get("edges").findWhere({"delete_scheduled":d.id});
+ });
+ }, 500);
+
+ if (this.minimap) {
+ window.setInterval(function() {
+ _this.rescaleMinimap();
+ }, 2000);
+ }
+
+};
+
+Rkns.Renderer.Scene.prototype.template = Rkns._.template(
+ '<% if (options.show_top_bar) { %><div class="Rk-TopBar"><% if (!options.editor_mode) { %><h2 class="Rk-PadTitle"><%- project.get("title") || translate("Untitled project")%></h2>'
+ + '<% } else { %><input type="text" class="Rk-PadTitle" value="<%- project.get("title") || "" %>" placeholder="<%-translate("Untitled project")%>" /><% } %>'
+ + '<div class="Rk-Users"><div class="Rk-CurrentUser"><span class="Rk-CurrentUser-Color"></span><span class="Rk-CurrentUser-Name"><unknown user></span></div><ul class="Rk-UserList"></ul></div>'
+ + '<div class="Rk-TopBar-Separator"></div><div class="Rk-TopBar-Button Rk-FullScreen-Button"><div class="Rk-TopBar-Tooltip"><div class="Rk-TopBar-Tooltip-Tip"></div><div class="Rk-TopBar-Tooltip-Contents"><%-translate("Full Screen")%></div></div></div>'
+ + '<% if (options.editor_mode) { %>'
+ + '<div class="Rk-TopBar-Separator"></div><div class="Rk-TopBar-Button Rk-AddNode-Button"><div class="Rk-TopBar-Tooltip"><div class="Rk-TopBar-Tooltip-Tip"></div><div class="Rk-TopBar-Tooltip-Contents"><%-translate("Add Node")%></div></div></div>'
+ + '<div class="Rk-TopBar-Separator"></div><div class="Rk-TopBar-Button Rk-AddEdge-Button"><div class="Rk-TopBar-Tooltip"><div class="Rk-TopBar-Tooltip-Tip"></div><div class="Rk-TopBar-Tooltip-Contents"><%-translate("Add Edge")%></div></div></div>'
+ + '<div class="Rk-TopBar-Separator"></div><div class="Rk-TopBar-Button Rk-Save-Button"><div class="Rk-TopBar-Tooltip"><div class="Rk-TopBar-Tooltip-Tip"></div><div class="Rk-TopBar-Tooltip-Contents"> </div></div></div>'
+ + '<div class="Rk-TopBar-Separator"></div><a class="Rk-TopBar-Button Rk-Bookmarklet-Button" href="#"><div class="Rk-TopBar-Tooltip"><div class="Rk-TopBar-Tooltip-Tip"></div><div class="Rk-TopBar-Tooltip-Contents">'
+ + '<%-translate("Renkan \'Drag-to-Add\' bookmarklet")%></div></div></a>'
+ + '<div class="Rk-TopBar-Separator"></div>'
+ + '<% } %></div><% } %>'
+ + '<div class="Rk-Editing-Space<% if (!options.show_top_bar) { %> Rk-Editing-Space-Full<% } %>">'
+ + '<div class="Rk-Labels"></div><canvas class="Rk-Canvas" resize></canvas><div class="Rk-Editor"><div class="Rk-Notifications"></div>'
+ + '<% if (options.show_bins) { %><div class="Rk-Fold-Bins">«</div><% } %>'
+ + '<div class="Rk-ZoomButtons"><div class="Rk-ZoomIn" title="<%-translate("Zoom In")%>"></div><div class="Rk-ZoomOut" title="<%-translate("Zoom Out")%>"></div></div>'
+ + '</div></div>'
+);
+
+Rkns.Renderer.Scene.prototype.fixSize = function(_autoscale) {
+ var w = this.$.width(),
+ h = this.$.height();
+ if (this.renkan.options.show_top_bar) {
+ h -= this.$.find(".Rk-TopBar").height();
+ }
+ this.canvas_$.attr({
+ width: w,
+ height: h
+ });
+
+ paper.view.viewSize = new paper.Size([w, h]);
+
+ if (_autoscale) {
+ this.autoScale();
+ }
+};
+
+Rkns.Renderer.Scene.prototype.drawSector = function(_repr, _inR, _outR, _startAngle, _endAngle, _padding, _imgname, _caption) {
+ var _options = this.renkan.options,
+ _startRads = _startAngle * Math.PI / 180,
+ _endRads = _endAngle * Math.PI / 180,
+ _img = this.icon_cache[_imgname],
+ _span = _endRads - _startRads,
+ _startdx = - Math.sin(_startRads),
+ _startdy = Math.cos(_startRads),
+ _startXIn = Math.cos(_startRads) * _inR + _padding * _startdx,
+ _startYIn = Math.sin(_startRads) * _inR + _padding * _startdy,
+ _startXOut = Math.cos(_startRads) * _outR + _padding * _startdx,
+ _startYOut = Math.sin(_startRads) * _outR + _padding * _startdy,
+ _enddx = - Math.sin(_endRads),
+ _enddy = Math.cos(_endRads),
+ _endXIn = Math.cos(_endRads) * _inR - _padding * _enddx,
+ _endYIn = Math.sin(_endRads) * _inR - _padding * _enddy,
+ _endXOut = Math.cos(_endRads) * _outR - _padding * _enddx,
+ _endYOut = Math.sin(_endRads) * _outR - _padding * _enddy,
+ _centerR = (_inR + _outR)/2,
+ _centerRads = (_startRads + _endRads) / 2,
+ _centerX = Math.cos(_centerRads) * _centerR,
+ _centerY = Math.sin(_centerRads) * _centerR,
+ _centerXIn = Math.cos(_centerRads) * _inR,
+ _centerXOut = Math.cos(_centerRads) * _outR,
+ _centerYIn = Math.sin(_centerRads) * _inR,
+ _centerYOut = Math.sin(_centerRads) * _outR,
+ _textX = Math.cos(_centerRads) * (_outR + 3),
+ _textY = Math.sin(_centerRads) * (_outR + _options.buttons_label_font_size) + _options.buttons_label_font_size / 2,
+ _segments = [];
+ this.buttons_layer.activate();
+ var _path = new paper.Path();
+ _path.add([_startXIn, _startYIn]);
+ _path.arcTo([_centerXIn, _centerYIn], [_endXIn, _endYIn]);
+ _path.lineTo([_endXOut, _endYOut]);
+ _path.arcTo([_centerXOut, _centerYOut], [_startXOut, _startYOut]);
+ _path.fillColor = _options.buttons_background;
+ _path.opacity = .5;
+ _path.closed = true;
+ _path.__representation = _repr;
+ var _text = new paper.PointText(_textX,_textY);
+ _text.characterStyle = {
+ fontSize: _options.buttons_label_font_size,
+ fillColor: _options.buttons_label_color
+ };
+ if (_textX > 2) {
+ _text.paragraphStyle.justification = 'left';
+ } else if (_textX < -2) {
+ _text.paragraphStyle.justification = 'right';
+ } else {
+ _text.paragraphStyle.justification = 'center';
+ }
+ _text.visible = false;
+ var _visible = false,
+ _restPos = new paper.Point(-200, -200),
+ _grp = new paper.Group([_path, _text]),
+ _delta = _grp.position,
+ _imgdelta = new paper.Point([_centerX, _centerY]),
+ _currentPos = new paper.Point(0,0);
+ _text.content = _caption;
+ _grp.visible = false;
+ _grp.position = _restPos;
+ var _res = {
+ show: function() {
+ _visible = true;
+ _grp.position = _currentPos.add(_delta);
+ _grp.visible = true;
+ },
+ moveTo: function(_point) {
+ _currentPos = _point;
+ if (_visible) {
+ _grp.position = _point.add(_delta);
+ }
+ },
+ hide: function() {
+ _visible = false;
+ _grp.visible = false;
+ _grp.position = _restPos;
+ },
+ select: function() {
+ _path.opacity = .8;
+ _text.visible = true;
+ },
+ unselect: function() {
+ _path.opacity = .5;
+ _text.visible = false;
+ },
+ destroy: function() {
+ _grp.remove();
+ }
+ };
+ function showImage() {
+ var _raster = new paper.Raster(_img);
+ _raster.position = _imgdelta.add(_grp.position).subtract(_delta);
+ _grp.addChild(_raster);
+ }
+ if (_img.width) {
+ showImage();
+ } else {
+ Rkns.$(_img).on("load",showImage);
+ }
+
+ return _res;
+};
+
+Rkns.Renderer.Scene.prototype.addToBundles = function(_edgeRepr) {
+ var _bundle = Rkns._(this.bundles).find(function(_bundle) {
+ return (
+ ( _bundle.from === _edgeRepr.from_representation && _bundle.to === _edgeRepr.to_representation )
+ || ( _bundle.from === _edgeRepr.to_representation && _bundle.to === _edgeRepr.from_representation )
+ );
+ });
+ if (typeof _bundle !== "undefined") {
+ _bundle.edges.push(_edgeRepr);
+ } else {
+ _bundle = {
+ from: _edgeRepr.from_representation,
+ to: _edgeRepr.to_representation,
+ edges: [ _edgeRepr ],
+ getPosition: function(_er) {
+ var _dir = (_er.from_representation === this.from) ? 1 : -1;
+ return _dir * ( Rkns._(this.edges).indexOf(_er) - (this.edges.length - 1) / 2 );
+ }
+ };
+ this.bundles.push(_bundle);
+ }
+ return _bundle;
+};
+
+Rkns.Renderer.Scene.prototype.isEditable = function() {
+ return (this.renkan.options.editor_mode && !this.renkan.read_only);
+};
+
+Rkns.Renderer.Scene.prototype.onStatusChange = function() {
+ var savebtn = this.$.find(".Rk-Save-Button"),
+ tip = savebtn.find(".Rk-TopBar-Tooltip-Contents");
+ if (this.renkan.read_only) {
+ savebtn.removeClass("disabled Rk-Save-Online").addClass("Rk-Save-ReadOnly");
+ tip.text(this.renkan.translate("Connection lost"));
+ } else {
+ if (this.renkan.options.snapshot_mode) {
+ savebtn.removeClass("Rk-Save-ReadOnly Rk-Save-Online");
+ tip.text(this.renkan.translate("Archive Project"));
+ } else {
+ savebtn.removeClass("disabled Rk-Save-ReadOnly").addClass("Rk-Save-Online");
+ tip.text(this.renkan.translate("Auto-save enabled"));
+ }
+ }
+};
+
+Rkns.Renderer.Scene.prototype.setScale = function(_newScale, _offset) {
+ if (_newScale > Rkns.Renderer._MIN_SCALE && _newScale < Rkns.Renderer._MAX_SCALE) {
+ this.scale = _newScale;
+ if (_offset) {
+ this.offset = _offset;
+ }
+ this.redraw();
+ }
+};
+
+Rkns.Renderer.Scene.prototype.autoScale = function() {
+ var nodes = this.renkan.project.get("nodes");
+ if (nodes.length > 1) {
+ var _xx = nodes.map(function(_node) { return _node.get("position").x; }),
+ _yy = nodes.map(function(_node) { return _node.get("position").y; }),
+ _minx = Math.min.apply(Math, _xx),
+ _miny = Math.min.apply(Math, _yy),
+ _maxx = Math.max.apply(Math, _xx),
+ _maxy = Math.max.apply(Math, _yy);
+ var _scale = Math.max(Rkns.Renderer._MIN_SCALE, Math.min(Rkns.Renderer._MAX_SCALE, (paper.view.size.width - 2 * this.renkan.options.autoscale_padding) / (_maxx - _minx), (paper.view.size.height - 2 * this.renkan.options.autoscale_padding) / (_maxy - _miny)));
+ this.setScale(_scale, paper.view.center.subtract(new paper.Point([(_maxx + _minx) / 2, (_maxy + _miny) / 2]).multiply(_scale)));
+ }
+ if (nodes.length === 1) {
+ this.setScale(1, paper.view.center.subtract(new paper.Point([nodes.at(0).get("position").x, nodes.at(0).get("position").y])));
+ }
+};
+
+Rkns.Renderer.Scene.prototype.redrawMiniframe = function() {
+ var topleft = this.toMinimapCoords(this.toModelCoords(new paper.Point([0,0]))),
+ bottomright = this.toMinimapCoords(this.toModelCoords(paper.view.bounds.bottomRight));
+ this.minimap.miniframe.fitBounds(topleft, bottomright);
+};
+
+Rkns.Renderer.Scene.prototype.rescaleMinimap = function() {
+ var nodes = this.renkan.project.get("nodes");
+ if (nodes.length > 1) {
+ var _xx = nodes.map(function(_node) { return _node.get("position").x; }),
+ _yy = nodes.map(function(_node) { return _node.get("position").y; }),
+ _minx = Math.min.apply(Math, _xx),
+ _miny = Math.min.apply(Math, _yy),
+ _maxx = Math.max.apply(Math, _xx),
+ _maxy = Math.max.apply(Math, _yy);
+ var _scale = Math.min(
+ this.scale * .8 * this.renkan.options.minimap_width / paper.view.bounds.width,
+ this.scale * .8 * this.renkan.options.minimap_height / paper.view.bounds.height,
+ ( this.renkan.options.minimap_width - 2 * this.renkan.options.minimap_padding ) / (_maxx - _minx),
+ ( this.renkan.options.minimap_height - 2 * this.renkan.options.minimap_padding ) / (_maxy - _miny)
+ );
+ this.minimap.offset = this.minimap.size.divide(2).subtract(new paper.Point([(_maxx + _minx) / 2, (_maxy + _miny) / 2]).multiply(_scale));
+ this.minimap.scale = _scale;
+ }
+ if (nodes.length === 1) {
+ this.minimap.scale = .1;
+ this.minimap.offset = this.minimap.size.divide(2).subtract(new paper.Point([nodes.at(0).get("position").x, nodes.at(0).get("position").y]).multiply(this.minimap.scale));
+ }
+ this.redraw();
+};
+
+Rkns.Renderer.Scene.prototype.toPaperCoords = function(_point) {
+ return _point.multiply(this.scale).add(this.offset);
+};
+
+Rkns.Renderer.Scene.prototype.toMinimapCoords = function(_point) {
+ return _point.multiply(this.minimap.scale).add(this.minimap.offset).add(this.minimap.topleft);
+};
+
+Rkns.Renderer.Scene.prototype.toModelCoords = function(_point) {
+ return _point.subtract(this.offset).divide(this.scale);
+};
+
+Rkns.Renderer.Scene.prototype.addRepresentation = function(_type, _model) {
+ var _repr = new Rkns.Renderer[_type](this, _model);
+ this.representations.push(_repr);
+ return _repr;
+};
+
+Rkns.Renderer.Scene.prototype.addRepresentations = function(_type, _collection) {
+ var _this = this;
+ _collection.forEach(function(_model) {
+ _this.addRepresentation(_type, _model);
+ });
+};
+
+Rkns.Renderer.Scene.prototype.userTemplate = Rkns._.template(
+ '<li class="Rk-User"><span class="Rk-UserColor" style="background:<%=background%>;"></span><%=name%></li>'
+);
+
+Rkns.Renderer.Scene.prototype.addUser = function(_user) {
+ if (_user.get("_id") === this.renkan.current_user) {
+ this.$.find(".Rk-CurrentUser-Name").text(_user.get("title"));
+ this.$.find(".Rk-CurrentUser-Color").css("background", _user.get("color"));
+ } else {
+ this.$.find(".Rk-UserList").append(
+ Rkns.$(
+ this.userTemplate({
+ name: _user.get("title"),
+ background: _user.get("color")
+ })
+ )
+ );
+ }
+};
+
+Rkns.Renderer.Scene.prototype.removeRepresentation = function(_representation) {
+ _representation.destroy();
+ this.representations = Rkns._(this.representations).reject(
+ function(_repr) {
+ return _repr == _representation;
+ }
+ );
+};
+
+Rkns.Renderer.Scene.prototype.getRepresentationByModel = function(_model) {
+ if (!_model) {
+ return undefined;
+ }
+ return Rkns._(this.representations).find(function(_repr) {
+ return _repr.model === _model;
+ });
+};
+
+Rkns.Renderer.Scene.prototype.removeRepresentationsOfType = function(_type) {
+ var _representations = Rkns._(this.representations).filter(function(_repr) {
+ return _repr.type == _type;
+ }),
+ _this = this;
+ Rkns._(_representations).each(function(_repr) {
+ _this.removeRepresentation(_repr);
+ });
+};
+
+Rkns.Renderer.Scene.prototype.highlightModel = function(_model) {
+ var _repr = this.getRepresentationByModel(_model);
+ if (_repr) {
+ _repr.highlight();
+ }
+};
+
+Rkns.Renderer.Scene.prototype.unhighlightAll = function(_model) {
+ Rkns._(this.representations).each(function(_repr) {
+ _repr.unhighlight();
+ });
+};
+
+Rkns.Renderer.Scene.prototype.unselectAll = function(_model) {
+ Rkns._(this.representations).each(function(_repr) {
+ _repr.unselect();
+ });
+};
+
+Rkns.Renderer.Scene.prototype.redraw = function() {
+ Rkns._(this.representations).each(function(_representation) {
+ _representation.redraw(true);
+ });
+ if (this.minimap) {
+ this.redrawMiniframe();
+ }
+ paper.view.draw();
+};
+
+Rkns.Renderer.Scene.prototype.addTempEdge = function(_from, _point) {
+ var _tmpEdge = this.addRepresentation("TempEdge",null);
+ _tmpEdge.end_pos = _point;
+ _tmpEdge.from_representation = _from;
+ _tmpEdge.redraw();
+ this.click_target = _tmpEdge;
+};
+
+Rkns.Renderer.Scene.prototype.findTarget = function(_hitResult) {
+ if (_hitResult && typeof _hitResult.item.__representation !== "undefined") {
+ var _newTarget = _hitResult.item.__representation;
+ if (this.selected_target !== _hitResult.item.__representation) {
+ if (this.selected_target) {
+ this.selected_target.unselect(_newTarget);
+ }
+ _newTarget.select(this.selected_target);
+ this.selected_target = _newTarget;
+ }
+ } else {
+ if (this.selected_target) {
+ this.selected_target.unselect();
+ }
+ this.selected_target = null;
+ }
+};
+
+Rkns.Renderer.Scene.prototype.paperShift = function(_delta) {
+ this.offset = this.offset.add(_delta);
+ this.redraw();
+};
+
+Rkns.Renderer.Scene.prototype.onMouseMove = function(_event) {
+ var _off = this.canvas_$.offset(),
+ _point = new paper.Point([
+ _event.pageX - _off.left,
+ _event.pageY - _off.top
+ ]),
+ _delta = _point.subtract(this.last_point);
+ this.last_point = _point;
+ if (!this.is_dragging && this.mouse_down && _delta.length > Rkns.Renderer._MIN_DRAG_DISTANCE) {
+ this.is_dragging = true;
+ }
+ var _hitResult = paper.project.hitTest(_point);
+ if (this.is_dragging) {
+ if (this.click_target && typeof this.click_target.paperShift === "function") {
+ this.click_target.paperShift(_delta);
+ } else {
+ this.paperShift(_delta);
+ }
+ } else {
+ this.findTarget(_hitResult);
+ }
+ paper.view.draw();
+};
+
+Rkns.Renderer.Scene.prototype.onMouseDown = function(_event, _isTouch) {
+ var _off = this.canvas_$.offset(),
+ _point = new paper.Point([
+ _event.pageX - _off.left,
+ _event.pageY - _off.top
+ ]);
+ this.last_point = _point;
+ this.mouse_down = true;
+ if (!this.click_target || this.click_target.type !== "Temp-edge") {
+ this.removeRepresentationsOfType("editor");
+ this.is_dragging = false;
+ var _hitResult = paper.project.hitTest(_point);
+ if (_hitResult && typeof _hitResult.item.__representation !== "undefined") {
+ this.click_target = _hitResult.item.__representation;
+ this.click_target.mousedown(_event, _isTouch);
+ } else {
+ this.click_target = null;
+ if (this.isEditable() && this.click_mode === Rkns.Renderer._CLICKMODE_ADDNODE) {
+ var _coords = this.toModelCoords(_point),
+ _data = {
+ id: Rkns.Utils.getUID('node'),
+ created_by: this.renkan.current_user,
+ position: {
+ x: _coords.x,
+ y: _coords.y
+ }
+ };
+ _node = this.renkan.project.addNode(_data);
+ this.getRepresentationByModel(_node).openEditor();
+ }
+ }
+ }
+ if (this.click_mode) {
+ if (this.isEditable() && this.click_mode === Rkns.Renderer._CLICKMODE_STARTEDGE && this.click_target && this.click_target.type === "Node") {
+ this.removeRepresentationsOfType("editor");
+ this.addTempEdge(this.click_target, _point);
+ this.click_mode = Rkns.Renderer._CLICKMODE_ENDEDGE;
+ this.notif_$.fadeOut(function() {
+ Rkns.$(this).html(_renkan.translate("Click on a second node to complete the edge")).fadeIn();
+ });
+ } else {
+ this.notif_$.hide();
+ this.click_mode = false;
+ }
+ }
+ paper.view.draw();
+};
+
+Rkns.Renderer.Scene.prototype.onMouseUp = function(_event, _isTouch) {
+ this.mouse_down = false;
+ if (this.click_target) {
+ var _off = this.canvas_$.offset();
+ this.click_target.mouseup(
+ {
+ point: new paper.Point([
+ _event.pageX - _off.left,
+ _event.pageY - _off.top
+ ])
+ },
+ _isTouch
+ );
+ } else {
+ this.click_target = null;
+ this.is_dragging = false;
+ if (_isTouch) {
+ this.unselectAll();
+ }
+ }
+ paper.view.draw();
+};
+
+Rkns.Renderer.Scene.prototype.onScroll = function(_event, _scrolldelta) {
+ this.totalScroll += _scrolldelta;
+ if (Math.abs(this.totalScroll) >= 1) {
+ var _off = this.canvas_$.offset(),
+ _delta = new paper.Point([
+ _event.pageX - _off.left,
+ _event.pageY - _off.top
+ ]).subtract(this.offset).multiply( Math.SQRT2 - 1 );
+ if (this.totalScroll > 0) {
+ this.setScale( this.scale * Math.SQRT2, this.offset.subtract(_delta) );
+ } else {
+ this.setScale( this.scale * Math.SQRT1_2, this.offset.add(_delta.divide(Math.SQRT2)));
+ }
+ this.totalScroll = 0;
+ }
+};
+
+Rkns.Renderer.Scene.prototype.onDoubleClick = function(_event) {
+ if (!this.isEditable()) {
+ return;
+ }
+ var _off = this.canvas_$.offset(),
+ _point = new paper.Point([
+ _event.pageX - _off.left,
+ _event.pageY - _off.top
+ ]);
+ var _hitResult = paper.project.hitTest(_point);
+ if (this.isEditable() && (!_hitResult || typeof _hitResult.item.__representation === "undefined")) {
+ var _coords = this.toModelCoords(_point),
+ _data = {
+ id: Rkns.Utils.getUID('node'),
+ created_by: this.renkan.current_user,
+ position: {
+ x: _coords.x,
+ y: _coords.y
+ }
+ };
+ _node = this.renkan.project.addNode(_data);
+ this.getRepresentationByModel(_node).openEditor();
+ }
+ paper.view.draw();
+};
+
+Rkns.Renderer.Scene.prototype.dropData = function(_data, _event) {
+ if (!this.isEditable()) {
+ return;
+ }
+ if (_data["text/json"] || _data["application/json"]) {
+ try {
+ var jsondata = JSON.parse(_data["text/json"] || _data["application/json"]);
+ _(_data).extend(jsondata);
+ }
+ catch(e) {}
+ }
+ var newNode = {};
+ switch(_data["text/x-iri-specific-site"]) {
+ case "twitter":
+ var snippet = Rkns.$('<div>').html(_data["text/x-iri-selected-html"]),
+ tweetdiv = snippet.find(".tweet");
+ newNode.title = _renkan.translate("Tweet by ") + tweetdiv.attr("data-name");
+ newNode.uri = "http://twitter.com/" + tweetdiv.attr("data-screen-name") + "/status/" + tweetdiv.attr("data-tweet-id");
+ newNode.image = tweetdiv.find(".avatar").attr("src");
+ newNode.description = tweetdiv.find(".js-tweet-text:first").text();
+ break;
+ case "google":
+ var snippet = Rkns.$('<div>').html(_data["text/x-iri-selected-html"]);
+ newNode.title = snippet.find("h3:first").text().trim();
+ newNode.uri = snippet.find("h3 a").attr("href");
+ newNode.description = snippet.find(".st:first").text().trim();
+ break;
+ case undefined:
+ default:
+ if (_data["text/x-iri-source-uri"]) {
+ newNode.uri = _data["text/x-iri-source-uri"];
+ }
+ if (_data["text/plain"] || _data["text/x-iri-selected-text"]) {
+ newNode.description = (_data["text/plain"] || _data["text/x-iri-selected-text"]).replace(/[\s\n]+/gm,' ').trim();
+ }
+ if (_data["text/html"] || _data["text/x-iri-selected-html"]) {
+ var snippet = Rkns.$('<div>').html(_data["text/html"] || _data["text/x-iri-selected-html"]);
+ var _svgimgs = snippet.find("image");
+ if (_svgimgs.length) {
+ newNode.image = _svgimgs.attr("xlink:href");
+ }
+ var _svgpaths = snippet.find("path");
+ if (_svgpaths.length) {
+ newNode.clipPath = _svgpaths.attr("d");
+ }
+ var _imgs = snippet.find("img");
+ if (_imgs.length) {
+ newNode.image = _imgs[0].src;
+ }
+ var _as = snippet.find("a");
+ if (_as.length) {
+ newNode.uri = _as[0].href;
+ }
+ newNode.title = snippet.find("[title]").attr("title") || newNode.title;
+ newNode.description = snippet.text().replace(/[\s\n]+/gm,' ').trim();
+ }
+ if (_data["text/uri-list"]) {
+ newNode.uri = _data["text/uri-list"];
+ }
+ if (_data["text/x-moz-url"] && !newNode.title) {
+ newNode.title = (_data["text/x-moz-url"].split("\n")[1] || "").trim();
+ if (newNode.title === newNode.uri) {
+ newNode.title = false;
+ }
+ }
+ if (_data["text/x-iri-source-title"] && !newNode.title) {
+ newNode.title = _data["text/x-iri-source-title"];
+ }
+ if (_data["text/html"] || _data["text/x-iri-selected-html"]) {
+ newNode.image = snippet.find("[data-image]").attr("data-image") || newNode.image;
+ newNode.uri = snippet.find("[data-uri]").attr("data-uri") || newNode.uri;
+ newNode.title = snippet.find("[data-title]").attr("data-title") || newNode.title;
+ newNode.description = snippet.find("[data-description]").attr("data-description") || newNode.description;
+ newNode.description = snippet.find("[data-clip-path]").attr("data-clip-path") || newNode.description;
+ }
+ }
+ if (!newNode.title) {
+ newNode.title = this.renkan.translate("Dragged resource");
+ }
+ var fields = ["title", "description", "uri", "image"];
+ for (var i = 0; i < fields.length; i++) {
+ var f = fields[i];
+ if (_data["text/x-iri-" + f] || _data[f]) {
+ newNode[f] = _data["text/x-iri-" + f] || _data[f];
+ }
+ if (newNode[f] === "none" || newNode[f] === "null") {
+ newNode[f] = undefined;
+ }
+ }
+ var _off = this.canvas_$.offset(),
+ _point = new paper.Point([
+ _event.pageX - _off.left,
+ _event.pageY - _off.top
+ ]),
+ _coords = this.toModelCoords(_point),
+ _nodedata = {
+ id: Rkns.Utils.getUID('node'),
+ created_by: this.renkan.current_user,
+ uri: newNode.uri || "",
+ title: newNode.title || "",
+ description: newNode.description || "",
+ image: newNode.image || "",
+ color: newNode.color || undefined,
+ "clip-path": newNode.clipPath || undefined,
+ position: {
+ x: _coords.x,
+ y: _coords.y
+ }
+ };
+ var _node = this.renkan.project.addNode(_nodedata),
+ _repr = this.getRepresentationByModel(_node);
+ if (_event.type === "drop") {
+ _repr.openEditor();
+ }
+};
+
+ /* *********************************************************
+ end of paper-renderer.js
+ ************************************************************
+ ************************************************************
+ start of full-json.js
+ ********************************************************* */
+
+/* Saves the Full JSON at each modification */
+
+Rkns.jsonIO = function(_renkan, _opts) {
+ var _proj = _renkan.project;
+ if (typeof _opts.http_method == "undefined") {
+ _opts.http_method = 'PUT';
+ }
+ var _load = function() {
+ Rkns.$.getJSON(_opts.url, function(_data) {
+ _proj.set(_data, {validate: true});
+ _renkan.renderer.autoScale();
+ });
+ };
+ var _save = function() {
+ var _data = _proj.toJSON();
+ if (!_renkan.read_only) {
+ Rkns.$.ajax({
+ type: _opts.http_method,
+ url: _opts.url,
+ contentType: "application/json",
+ data: JSON.stringify(_data),
+ success: function(data, textStatus, jqXHR) {
+ }
+ });
+ }
+
+ };
+ var _thrSave = Rkns._.throttle(
+ function() {
+ setTimeout(_save, 100);
+ }, 1000);
+ _proj.on("add:nodes add:edges add:users", function(_model) {
+ _model.on("change remove", function(_model) {
+ _thrSave();
+ });
+ _thrSave();
+ });
+ _proj.on("change", function() {
+ _thrSave();
+ });
+
+ _load();
+};
+
+ /* *********************************************************
+ end of full-json.js
+ ************************************************************
+ */
--- a/web/res/js/underscore-min.js Wed Apr 10 18:38:21 2013 +0200
+++ b/web/res/js/underscore-min.js Fri Jun 07 11:55:46 2013 +0200
@@ -1,32 +1,1 @@
-// Underscore.js 1.3.3
-// (c) 2009-2012 Jeremy Ashkenas, DocumentCloud Inc.
-// Underscore is freely distributable under the MIT license.
-// Portions of Underscore are inspired or borrowed from Prototype,
-// Oliver Steele's Functional, and John Resig's Micro-Templating.
-// For all details and documentation:
-// http://documentcloud.github.com/underscore
-(function(){function r(a,c,d){if(a===c)return 0!==a||1/a==1/c;if(null==a||null==c)return a===c;a._chain&&(a=a._wrapped);c._chain&&(c=c._wrapped);if(a.isEqual&&b.isFunction(a.isEqual))return a.isEqual(c);if(c.isEqual&&b.isFunction(c.isEqual))return c.isEqual(a);var e=l.call(a);if(e!=l.call(c))return!1;switch(e){case "[object String]":return a==""+c;case "[object Number]":return a!=+a?c!=+c:0==a?1/a==1/c:a==+c;case "[object Date]":case "[object Boolean]":return+a==+c;case "[object RegExp]":return a.source==
-c.source&&a.global==c.global&&a.multiline==c.multiline&&a.ignoreCase==c.ignoreCase}if("object"!=typeof a||"object"!=typeof c)return!1;for(var f=d.length;f--;)if(d[f]==a)return!0;d.push(a);var f=0,g=!0;if("[object Array]"==e){if(f=a.length,g=f==c.length)for(;f--&&(g=f in a==f in c&&r(a[f],c[f],d)););}else{if("constructor"in a!="constructor"in c||a.constructor!=c.constructor)return!1;for(var h in a)if(b.has(a,h)&&(f++,!(g=b.has(c,h)&&r(a[h],c[h],d))))break;if(g){for(h in c)if(b.has(c,h)&&!f--)break;
-g=!f}}d.pop();return g}var s=this,I=s._,o={},k=Array.prototype,p=Object.prototype,i=k.slice,J=k.unshift,l=p.toString,K=p.hasOwnProperty,y=k.forEach,z=k.map,A=k.reduce,B=k.reduceRight,C=k.filter,D=k.every,E=k.some,q=k.indexOf,F=k.lastIndexOf,p=Array.isArray,L=Object.keys,t=Function.prototype.bind,b=function(a){return new m(a)};"undefined"!==typeof exports?("undefined"!==typeof module&&module.exports&&(exports=module.exports=b),exports._=b):s._=b;b.VERSION="1.3.3";var j=b.each=b.forEach=function(a,
-c,d){if(a!=null)if(y&&a.forEach===y)a.forEach(c,d);else if(a.length===+a.length)for(var e=0,f=a.length;e<f;e++){if(e in a&&c.call(d,a[e],e,a)===o)break}else for(e in a)if(b.has(a,e)&&c.call(d,a[e],e,a)===o)break};b.map=b.collect=function(a,c,b){var e=[];if(a==null)return e;if(z&&a.map===z)return a.map(c,b);j(a,function(a,g,h){e[e.length]=c.call(b,a,g,h)});if(a.length===+a.length)e.length=a.length;return e};b.reduce=b.foldl=b.inject=function(a,c,d,e){var f=arguments.length>2;a==null&&(a=[]);if(A&&
-a.reduce===A){e&&(c=b.bind(c,e));return f?a.reduce(c,d):a.reduce(c)}j(a,function(a,b,i){if(f)d=c.call(e,d,a,b,i);else{d=a;f=true}});if(!f)throw new TypeError("Reduce of empty array with no initial value");return d};b.reduceRight=b.foldr=function(a,c,d,e){var f=arguments.length>2;a==null&&(a=[]);if(B&&a.reduceRight===B){e&&(c=b.bind(c,e));return f?a.reduceRight(c,d):a.reduceRight(c)}var g=b.toArray(a).reverse();e&&!f&&(c=b.bind(c,e));return f?b.reduce(g,c,d,e):b.reduce(g,c)};b.find=b.detect=function(a,
-c,b){var e;G(a,function(a,g,h){if(c.call(b,a,g,h)){e=a;return true}});return e};b.filter=b.select=function(a,c,b){var e=[];if(a==null)return e;if(C&&a.filter===C)return a.filter(c,b);j(a,function(a,g,h){c.call(b,a,g,h)&&(e[e.length]=a)});return e};b.reject=function(a,c,b){var e=[];if(a==null)return e;j(a,function(a,g,h){c.call(b,a,g,h)||(e[e.length]=a)});return e};b.every=b.all=function(a,c,b){var e=true;if(a==null)return e;if(D&&a.every===D)return a.every(c,b);j(a,function(a,g,h){if(!(e=e&&c.call(b,
-a,g,h)))return o});return!!e};var G=b.some=b.any=function(a,c,d){c||(c=b.identity);var e=false;if(a==null)return e;if(E&&a.some===E)return a.some(c,d);j(a,function(a,b,h){if(e||(e=c.call(d,a,b,h)))return o});return!!e};b.include=b.contains=function(a,c){var b=false;if(a==null)return b;if(q&&a.indexOf===q)return a.indexOf(c)!=-1;return b=G(a,function(a){return a===c})};b.invoke=function(a,c){var d=i.call(arguments,2);return b.map(a,function(a){return(b.isFunction(c)?c||a:a[c]).apply(a,d)})};b.pluck=
-function(a,c){return b.map(a,function(a){return a[c]})};b.max=function(a,c,d){if(!c&&b.isArray(a)&&a[0]===+a[0])return Math.max.apply(Math,a);if(!c&&b.isEmpty(a))return-Infinity;var e={computed:-Infinity};j(a,function(a,b,h){b=c?c.call(d,a,b,h):a;b>=e.computed&&(e={value:a,computed:b})});return e.value};b.min=function(a,c,d){if(!c&&b.isArray(a)&&a[0]===+a[0])return Math.min.apply(Math,a);if(!c&&b.isEmpty(a))return Infinity;var e={computed:Infinity};j(a,function(a,b,h){b=c?c.call(d,a,b,h):a;b<e.computed&&
-(e={value:a,computed:b})});return e.value};b.shuffle=function(a){var b=[],d;j(a,function(a,f){d=Math.floor(Math.random()*(f+1));b[f]=b[d];b[d]=a});return b};b.sortBy=function(a,c,d){var e=b.isFunction(c)?c:function(a){return a[c]};return b.pluck(b.map(a,function(a,b,c){return{value:a,criteria:e.call(d,a,b,c)}}).sort(function(a,b){var c=a.criteria,d=b.criteria;return c===void 0?1:d===void 0?-1:c<d?-1:c>d?1:0}),"value")};b.groupBy=function(a,c){var d={},e=b.isFunction(c)?c:function(a){return a[c]};
-j(a,function(a,b){var c=e(a,b);(d[c]||(d[c]=[])).push(a)});return d};b.sortedIndex=function(a,c,d){d||(d=b.identity);for(var e=0,f=a.length;e<f;){var g=e+f>>1;d(a[g])<d(c)?e=g+1:f=g}return e};b.toArray=function(a){return!a?[]:b.isArray(a)||b.isArguments(a)?i.call(a):a.toArray&&b.isFunction(a.toArray)?a.toArray():b.values(a)};b.size=function(a){return b.isArray(a)?a.length:b.keys(a).length};b.first=b.head=b.take=function(a,b,d){return b!=null&&!d?i.call(a,0,b):a[0]};b.initial=function(a,b,d){return i.call(a,
-0,a.length-(b==null||d?1:b))};b.last=function(a,b,d){return b!=null&&!d?i.call(a,Math.max(a.length-b,0)):a[a.length-1]};b.rest=b.tail=function(a,b,d){return i.call(a,b==null||d?1:b)};b.compact=function(a){return b.filter(a,function(a){return!!a})};b.flatten=function(a,c){return b.reduce(a,function(a,e){if(b.isArray(e))return a.concat(c?e:b.flatten(e));a[a.length]=e;return a},[])};b.without=function(a){return b.difference(a,i.call(arguments,1))};b.uniq=b.unique=function(a,c,d){var d=d?b.map(a,d):a,
-e=[];a.length<3&&(c=true);b.reduce(d,function(d,g,h){if(c?b.last(d)!==g||!d.length:!b.include(d,g)){d.push(g);e.push(a[h])}return d},[]);return e};b.union=function(){return b.uniq(b.flatten(arguments,true))};b.intersection=b.intersect=function(a){var c=i.call(arguments,1);return b.filter(b.uniq(a),function(a){return b.every(c,function(c){return b.indexOf(c,a)>=0})})};b.difference=function(a){var c=b.flatten(i.call(arguments,1),true);return b.filter(a,function(a){return!b.include(c,a)})};b.zip=function(){for(var a=
-i.call(arguments),c=b.max(b.pluck(a,"length")),d=Array(c),e=0;e<c;e++)d[e]=b.pluck(a,""+e);return d};b.indexOf=function(a,c,d){if(a==null)return-1;var e;if(d){d=b.sortedIndex(a,c);return a[d]===c?d:-1}if(q&&a.indexOf===q)return a.indexOf(c);d=0;for(e=a.length;d<e;d++)if(d in a&&a[d]===c)return d;return-1};b.lastIndexOf=function(a,b){if(a==null)return-1;if(F&&a.lastIndexOf===F)return a.lastIndexOf(b);for(var d=a.length;d--;)if(d in a&&a[d]===b)return d;return-1};b.range=function(a,b,d){if(arguments.length<=
-1){b=a||0;a=0}for(var d=arguments[2]||1,e=Math.max(Math.ceil((b-a)/d),0),f=0,g=Array(e);f<e;){g[f++]=a;a=a+d}return g};var H=function(){};b.bind=function(a,c){var d,e;if(a.bind===t&&t)return t.apply(a,i.call(arguments,1));if(!b.isFunction(a))throw new TypeError;e=i.call(arguments,2);return d=function(){if(!(this instanceof d))return a.apply(c,e.concat(i.call(arguments)));H.prototype=a.prototype;var b=new H,g=a.apply(b,e.concat(i.call(arguments)));return Object(g)===g?g:b}};b.bindAll=function(a){var c=
-i.call(arguments,1);c.length==0&&(c=b.functions(a));j(c,function(c){a[c]=b.bind(a[c],a)});return a};b.memoize=function(a,c){var d={};c||(c=b.identity);return function(){var e=c.apply(this,arguments);return b.has(d,e)?d[e]:d[e]=a.apply(this,arguments)}};b.delay=function(a,b){var d=i.call(arguments,2);return setTimeout(function(){return a.apply(null,d)},b)};b.defer=function(a){return b.delay.apply(b,[a,1].concat(i.call(arguments,1)))};b.throttle=function(a,c){var d,e,f,g,h,i,j=b.debounce(function(){h=
-g=false},c);return function(){d=this;e=arguments;f||(f=setTimeout(function(){f=null;h&&a.apply(d,e);j()},c));g?h=true:i=a.apply(d,e);j();g=true;return i}};b.debounce=function(a,b,d){var e;return function(){var f=this,g=arguments;d&&!e&&a.apply(f,g);clearTimeout(e);e=setTimeout(function(){e=null;d||a.apply(f,g)},b)}};b.once=function(a){var b=false,d;return function(){if(b)return d;b=true;return d=a.apply(this,arguments)}};b.wrap=function(a,b){return function(){var d=[a].concat(i.call(arguments,0));
-return b.apply(this,d)}};b.compose=function(){var a=arguments;return function(){for(var b=arguments,d=a.length-1;d>=0;d--)b=[a[d].apply(this,b)];return b[0]}};b.after=function(a,b){return a<=0?b():function(){if(--a<1)return b.apply(this,arguments)}};b.keys=L||function(a){if(a!==Object(a))throw new TypeError("Invalid object");var c=[],d;for(d in a)b.has(a,d)&&(c[c.length]=d);return c};b.values=function(a){return b.map(a,b.identity)};b.functions=b.methods=function(a){var c=[],d;for(d in a)b.isFunction(a[d])&&
-c.push(d);return c.sort()};b.extend=function(a){j(i.call(arguments,1),function(b){for(var d in b)a[d]=b[d]});return a};b.pick=function(a){var c={};j(b.flatten(i.call(arguments,1)),function(b){b in a&&(c[b]=a[b])});return c};b.defaults=function(a){j(i.call(arguments,1),function(b){for(var d in b)a[d]==null&&(a[d]=b[d])});return a};b.clone=function(a){return!b.isObject(a)?a:b.isArray(a)?a.slice():b.extend({},a)};b.tap=function(a,b){b(a);return a};b.isEqual=function(a,b){return r(a,b,[])};b.isEmpty=
-function(a){if(a==null)return true;if(b.isArray(a)||b.isString(a))return a.length===0;for(var c in a)if(b.has(a,c))return false;return true};b.isElement=function(a){return!!(a&&a.nodeType==1)};b.isArray=p||function(a){return l.call(a)=="[object Array]"};b.isObject=function(a){return a===Object(a)};b.isArguments=function(a){return l.call(a)=="[object Arguments]"};b.isArguments(arguments)||(b.isArguments=function(a){return!(!a||!b.has(a,"callee"))});b.isFunction=function(a){return l.call(a)=="[object Function]"};
-b.isString=function(a){return l.call(a)=="[object String]"};b.isNumber=function(a){return l.call(a)=="[object Number]"};b.isFinite=function(a){return b.isNumber(a)&&isFinite(a)};b.isNaN=function(a){return a!==a};b.isBoolean=function(a){return a===true||a===false||l.call(a)=="[object Boolean]"};b.isDate=function(a){return l.call(a)=="[object Date]"};b.isRegExp=function(a){return l.call(a)=="[object RegExp]"};b.isNull=function(a){return a===null};b.isUndefined=function(a){return a===void 0};b.has=function(a,
-b){return K.call(a,b)};b.noConflict=function(){s._=I;return this};b.identity=function(a){return a};b.times=function(a,b,d){for(var e=0;e<a;e++)b.call(d,e)};b.escape=function(a){return(""+a).replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'").replace(/\//g,"/")};b.result=function(a,c){if(a==null)return null;var d=a[c];return b.isFunction(d)?d.call(a):d};b.mixin=function(a){j(b.functions(a),function(c){M(c,b[c]=a[c])})};var N=0;b.uniqueId=
-function(a){var b=N++;return a?a+b:b};b.templateSettings={evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g,escape:/<%-([\s\S]+?)%>/g};var u=/.^/,n={"\\":"\\","'":"'",r:"\r",n:"\n",t:"\t",u2028:"\u2028",u2029:"\u2029"},v;for(v in n)n[n[v]]=v;var O=/\\|'|\r|\n|\t|\u2028|\u2029/g,P=/\\(\\|'|r|n|t|u2028|u2029)/g,w=function(a){return a.replace(P,function(a,b){return n[b]})};b.template=function(a,c,d){d=b.defaults(d||{},b.templateSettings);a="__p+='"+a.replace(O,function(a){return"\\"+n[a]}).replace(d.escape||
-u,function(a,b){return"'+\n_.escape("+w(b)+")+\n'"}).replace(d.interpolate||u,function(a,b){return"'+\n("+w(b)+")+\n'"}).replace(d.evaluate||u,function(a,b){return"';\n"+w(b)+"\n;__p+='"})+"';\n";d.variable||(a="with(obj||{}){\n"+a+"}\n");var a="var __p='';var print=function(){__p+=Array.prototype.join.call(arguments, '')};\n"+a+"return __p;\n",e=new Function(d.variable||"obj","_",a);if(c)return e(c,b);c=function(a){return e.call(this,a,b)};c.source="function("+(d.variable||"obj")+"){\n"+a+"}";return c};
-b.chain=function(a){return b(a).chain()};var m=function(a){this._wrapped=a};b.prototype=m.prototype;var x=function(a,c){return c?b(a).chain():a},M=function(a,c){m.prototype[a]=function(){var a=i.call(arguments);J.call(a,this._wrapped);return x(c.apply(b,a),this._chain)}};b.mixin(b);j("pop,push,reverse,shift,sort,splice,unshift".split(","),function(a){var b=k[a];m.prototype[a]=function(){var d=this._wrapped;b.apply(d,arguments);var e=d.length;(a=="shift"||a=="splice")&&e===0&&delete d[0];return x(d,
-this._chain)}});j(["concat","join","slice"],function(a){var b=k[a];m.prototype[a]=function(){return x(b.apply(this._wrapped,arguments),this._chain)}});m.prototype.chain=function(){this._chain=true;return this};m.prototype.value=function(){return this._wrapped}}).call(this);
+(function(){var n=this,t=n._,r={},e=Array.prototype,u=Object.prototype,i=Function.prototype,a=e.push,o=e.slice,c=e.concat,l=u.toString,f=u.hasOwnProperty,s=e.forEach,p=e.map,h=e.reduce,v=e.reduceRight,d=e.filter,g=e.every,m=e.some,y=e.indexOf,b=e.lastIndexOf,x=Array.isArray,_=Object.keys,j=i.bind,w=function(n){return n instanceof w?n:this instanceof w?(this._wrapped=n,void 0):new w(n)};"undefined"!=typeof exports?("undefined"!=typeof module&&module.exports&&(exports=module.exports=w),exports._=w):n._=w,w.VERSION="1.4.4";var A=w.each=w.forEach=function(n,t,e){if(null!=n)if(s&&n.forEach===s)n.forEach(t,e);else if(n.length===+n.length){for(var u=0,i=n.length;i>u;u++)if(t.call(e,n[u],u,n)===r)return}else for(var a in n)if(w.has(n,a)&&t.call(e,n[a],a,n)===r)return};w.map=w.collect=function(n,t,r){var e=[];return null==n?e:p&&n.map===p?n.map(t,r):(A(n,function(n,u,i){e[e.length]=t.call(r,n,u,i)}),e)};var O="Reduce of empty array with no initial value";w.reduce=w.foldl=w.inject=function(n,t,r,e){var u=arguments.length>2;if(null==n&&(n=[]),h&&n.reduce===h)return e&&(t=w.bind(t,e)),u?n.reduce(t,r):n.reduce(t);if(A(n,function(n,i,a){u?r=t.call(e,r,n,i,a):(r=n,u=!0)}),!u)throw new TypeError(O);return r},w.reduceRight=w.foldr=function(n,t,r,e){var u=arguments.length>2;if(null==n&&(n=[]),v&&n.reduceRight===v)return e&&(t=w.bind(t,e)),u?n.reduceRight(t,r):n.reduceRight(t);var i=n.length;if(i!==+i){var a=w.keys(n);i=a.length}if(A(n,function(o,c,l){c=a?a[--i]:--i,u?r=t.call(e,r,n[c],c,l):(r=n[c],u=!0)}),!u)throw new TypeError(O);return r},w.find=w.detect=function(n,t,r){var e;return E(n,function(n,u,i){return t.call(r,n,u,i)?(e=n,!0):void 0}),e},w.filter=w.select=function(n,t,r){var e=[];return null==n?e:d&&n.filter===d?n.filter(t,r):(A(n,function(n,u,i){t.call(r,n,u,i)&&(e[e.length]=n)}),e)},w.reject=function(n,t,r){return w.filter(n,function(n,e,u){return!t.call(r,n,e,u)},r)},w.every=w.all=function(n,t,e){t||(t=w.identity);var u=!0;return null==n?u:g&&n.every===g?n.every(t,e):(A(n,function(n,i,a){return(u=u&&t.call(e,n,i,a))?void 0:r}),!!u)};var E=w.some=w.any=function(n,t,e){t||(t=w.identity);var u=!1;return null==n?u:m&&n.some===m?n.some(t,e):(A(n,function(n,i,a){return u||(u=t.call(e,n,i,a))?r:void 0}),!!u)};w.contains=w.include=function(n,t){return null==n?!1:y&&n.indexOf===y?n.indexOf(t)!=-1:E(n,function(n){return n===t})},w.invoke=function(n,t){var r=o.call(arguments,2),e=w.isFunction(t);return w.map(n,function(n){return(e?t:n[t]).apply(n,r)})},w.pluck=function(n,t){return w.map(n,function(n){return n[t]})},w.where=function(n,t,r){return w.isEmpty(t)?r?null:[]:w[r?"find":"filter"](n,function(n){for(var r in t)if(t[r]!==n[r])return!1;return!0})},w.findWhere=function(n,t){return w.where(n,t,!0)},w.max=function(n,t,r){if(!t&&w.isArray(n)&&n[0]===+n[0]&&65535>n.length)return Math.max.apply(Math,n);if(!t&&w.isEmpty(n))return-1/0;var e={computed:-1/0,value:-1/0};return A(n,function(n,u,i){var a=t?t.call(r,n,u,i):n;a>=e.computed&&(e={value:n,computed:a})}),e.value},w.min=function(n,t,r){if(!t&&w.isArray(n)&&n[0]===+n[0]&&65535>n.length)return Math.min.apply(Math,n);if(!t&&w.isEmpty(n))return 1/0;var e={computed:1/0,value:1/0};return A(n,function(n,u,i){var a=t?t.call(r,n,u,i):n;e.computed>a&&(e={value:n,computed:a})}),e.value},w.shuffle=function(n){var t,r=0,e=[];return A(n,function(n){t=w.random(r++),e[r-1]=e[t],e[t]=n}),e};var k=function(n){return w.isFunction(n)?n:function(t){return t[n]}};w.sortBy=function(n,t,r){var e=k(t);return w.pluck(w.map(n,function(n,t,u){return{value:n,index:t,criteria:e.call(r,n,t,u)}}).sort(function(n,t){var r=n.criteria,e=t.criteria;if(r!==e){if(r>e||r===void 0)return 1;if(e>r||e===void 0)return-1}return n.index<t.index?-1:1}),"value")};var F=function(n,t,r,e){var u={},i=k(t||w.identity);return A(n,function(t,a){var o=i.call(r,t,a,n);e(u,o,t)}),u};w.groupBy=function(n,t,r){return F(n,t,r,function(n,t,r){(w.has(n,t)?n[t]:n[t]=[]).push(r)})},w.countBy=function(n,t,r){return F(n,t,r,function(n,t){w.has(n,t)||(n[t]=0),n[t]++})},w.sortedIndex=function(n,t,r,e){r=null==r?w.identity:k(r);for(var u=r.call(e,t),i=0,a=n.length;a>i;){var o=i+a>>>1;u>r.call(e,n[o])?i=o+1:a=o}return i},w.toArray=function(n){return n?w.isArray(n)?o.call(n):n.length===+n.length?w.map(n,w.identity):w.values(n):[]},w.size=function(n){return null==n?0:n.length===+n.length?n.length:w.keys(n).length},w.first=w.head=w.take=function(n,t,r){return null==n?void 0:null==t||r?n[0]:o.call(n,0,t)},w.initial=function(n,t,r){return o.call(n,0,n.length-(null==t||r?1:t))},w.last=function(n,t,r){return null==n?void 0:null==t||r?n[n.length-1]:o.call(n,Math.max(n.length-t,0))},w.rest=w.tail=w.drop=function(n,t,r){return o.call(n,null==t||r?1:t)},w.compact=function(n){return w.filter(n,w.identity)};var R=function(n,t,r){return A(n,function(n){w.isArray(n)?t?a.apply(r,n):R(n,t,r):r.push(n)}),r};w.flatten=function(n,t){return R(n,t,[])},w.without=function(n){return w.difference(n,o.call(arguments,1))},w.uniq=w.unique=function(n,t,r,e){w.isFunction(t)&&(e=r,r=t,t=!1);var u=r?w.map(n,r,e):n,i=[],a=[];return A(u,function(r,e){(t?e&&a[a.length-1]===r:w.contains(a,r))||(a.push(r),i.push(n[e]))}),i},w.union=function(){return w.uniq(c.apply(e,arguments))},w.intersection=function(n){var t=o.call(arguments,1);return w.filter(w.uniq(n),function(n){return w.every(t,function(t){return w.indexOf(t,n)>=0})})},w.difference=function(n){var t=c.apply(e,o.call(arguments,1));return w.filter(n,function(n){return!w.contains(t,n)})},w.zip=function(){for(var n=o.call(arguments),t=w.max(w.pluck(n,"length")),r=Array(t),e=0;t>e;e++)r[e]=w.pluck(n,""+e);return r},w.object=function(n,t){if(null==n)return{};for(var r={},e=0,u=n.length;u>e;e++)t?r[n[e]]=t[e]:r[n[e][0]]=n[e][1];return r},w.indexOf=function(n,t,r){if(null==n)return-1;var e=0,u=n.length;if(r){if("number"!=typeof r)return e=w.sortedIndex(n,t),n[e]===t?e:-1;e=0>r?Math.max(0,u+r):r}if(y&&n.indexOf===y)return n.indexOf(t,r);for(;u>e;e++)if(n[e]===t)return e;return-1},w.lastIndexOf=function(n,t,r){if(null==n)return-1;var e=null!=r;if(b&&n.lastIndexOf===b)return e?n.lastIndexOf(t,r):n.lastIndexOf(t);for(var u=e?r:n.length;u--;)if(n[u]===t)return u;return-1},w.range=function(n,t,r){1>=arguments.length&&(t=n||0,n=0),r=arguments[2]||1;for(var e=Math.max(Math.ceil((t-n)/r),0),u=0,i=Array(e);e>u;)i[u++]=n,n+=r;return i},w.bind=function(n,t){if(n.bind===j&&j)return j.apply(n,o.call(arguments,1));var r=o.call(arguments,2);return function(){return n.apply(t,r.concat(o.call(arguments)))}},w.partial=function(n){var t=o.call(arguments,1);return function(){return n.apply(this,t.concat(o.call(arguments)))}},w.bindAll=function(n){var t=o.call(arguments,1);return 0===t.length&&(t=w.functions(n)),A(t,function(t){n[t]=w.bind(n[t],n)}),n},w.memoize=function(n,t){var r={};return t||(t=w.identity),function(){var e=t.apply(this,arguments);return w.has(r,e)?r[e]:r[e]=n.apply(this,arguments)}},w.delay=function(n,t){var r=o.call(arguments,2);return setTimeout(function(){return n.apply(null,r)},t)},w.defer=function(n){return w.delay.apply(w,[n,1].concat(o.call(arguments,1)))},w.throttle=function(n,t){var r,e,u,i,a=0,o=function(){a=new Date,u=null,i=n.apply(r,e)};return function(){var c=new Date,l=t-(c-a);return r=this,e=arguments,0>=l?(clearTimeout(u),u=null,a=c,i=n.apply(r,e)):u||(u=setTimeout(o,l)),i}},w.debounce=function(n,t,r){var e,u;return function(){var i=this,a=arguments,o=function(){e=null,r||(u=n.apply(i,a))},c=r&&!e;return clearTimeout(e),e=setTimeout(o,t),c&&(u=n.apply(i,a)),u}},w.once=function(n){var t,r=!1;return function(){return r?t:(r=!0,t=n.apply(this,arguments),n=null,t)}},w.wrap=function(n,t){return function(){var r=[n];return a.apply(r,arguments),t.apply(this,r)}},w.compose=function(){var n=arguments;return function(){for(var t=arguments,r=n.length-1;r>=0;r--)t=[n[r].apply(this,t)];return t[0]}},w.after=function(n,t){return 0>=n?t():function(){return 1>--n?t.apply(this,arguments):void 0}},w.keys=_||function(n){if(n!==Object(n))throw new TypeError("Invalid object");var t=[];for(var r in n)w.has(n,r)&&(t[t.length]=r);return t},w.values=function(n){var t=[];for(var r in n)w.has(n,r)&&t.push(n[r]);return t},w.pairs=function(n){var t=[];for(var r in n)w.has(n,r)&&t.push([r,n[r]]);return t},w.invert=function(n){var t={};for(var r in n)w.has(n,r)&&(t[n[r]]=r);return t},w.functions=w.methods=function(n){var t=[];for(var r in n)w.isFunction(n[r])&&t.push(r);return t.sort()},w.extend=function(n){return A(o.call(arguments,1),function(t){if(t)for(var r in t)n[r]=t[r]}),n},w.pick=function(n){var t={},r=c.apply(e,o.call(arguments,1));return A(r,function(r){r in n&&(t[r]=n[r])}),t},w.omit=function(n){var t={},r=c.apply(e,o.call(arguments,1));for(var u in n)w.contains(r,u)||(t[u]=n[u]);return t},w.defaults=function(n){return A(o.call(arguments,1),function(t){if(t)for(var r in t)null==n[r]&&(n[r]=t[r])}),n},w.clone=function(n){return w.isObject(n)?w.isArray(n)?n.slice():w.extend({},n):n},w.tap=function(n,t){return t(n),n};var I=function(n,t,r,e){if(n===t)return 0!==n||1/n==1/t;if(null==n||null==t)return n===t;n instanceof w&&(n=n._wrapped),t instanceof w&&(t=t._wrapped);var u=l.call(n);if(u!=l.call(t))return!1;switch(u){case"[object String]":return n==t+"";case"[object Number]":return n!=+n?t!=+t:0==n?1/n==1/t:n==+t;case"[object Date]":case"[object Boolean]":return+n==+t;case"[object RegExp]":return n.source==t.source&&n.global==t.global&&n.multiline==t.multiline&&n.ignoreCase==t.ignoreCase}if("object"!=typeof n||"object"!=typeof t)return!1;for(var i=r.length;i--;)if(r[i]==n)return e[i]==t;r.push(n),e.push(t);var a=0,o=!0;if("[object Array]"==u){if(a=n.length,o=a==t.length)for(;a--&&(o=I(n[a],t[a],r,e)););}else{var c=n.constructor,f=t.constructor;if(c!==f&&!(w.isFunction(c)&&c instanceof c&&w.isFunction(f)&&f instanceof f))return!1;for(var s in n)if(w.has(n,s)&&(a++,!(o=w.has(t,s)&&I(n[s],t[s],r,e))))break;if(o){for(s in t)if(w.has(t,s)&&!a--)break;o=!a}}return r.pop(),e.pop(),o};w.isEqual=function(n,t){return I(n,t,[],[])},w.isEmpty=function(n){if(null==n)return!0;if(w.isArray(n)||w.isString(n))return 0===n.length;for(var t in n)if(w.has(n,t))return!1;return!0},w.isElement=function(n){return!(!n||1!==n.nodeType)},w.isArray=x||function(n){return"[object Array]"==l.call(n)},w.isObject=function(n){return n===Object(n)},A(["Arguments","Function","String","Number","Date","RegExp"],function(n){w["is"+n]=function(t){return l.call(t)=="[object "+n+"]"}}),w.isArguments(arguments)||(w.isArguments=function(n){return!(!n||!w.has(n,"callee"))}),"function"!=typeof/./&&(w.isFunction=function(n){return"function"==typeof n}),w.isFinite=function(n){return isFinite(n)&&!isNaN(parseFloat(n))},w.isNaN=function(n){return w.isNumber(n)&&n!=+n},w.isBoolean=function(n){return n===!0||n===!1||"[object Boolean]"==l.call(n)},w.isNull=function(n){return null===n},w.isUndefined=function(n){return n===void 0},w.has=function(n,t){return f.call(n,t)},w.noConflict=function(){return n._=t,this},w.identity=function(n){return n},w.times=function(n,t,r){for(var e=Array(n),u=0;n>u;u++)e[u]=t.call(r,u);return e},w.random=function(n,t){return null==t&&(t=n,n=0),n+Math.floor(Math.random()*(t-n+1))};var M={escape:{"&":"&","<":"<",">":">",'"':""","'":"'","/":"/"}};M.unescape=w.invert(M.escape);var S={escape:RegExp("["+w.keys(M.escape).join("")+"]","g"),unescape:RegExp("("+w.keys(M.unescape).join("|")+")","g")};w.each(["escape","unescape"],function(n){w[n]=function(t){return null==t?"":(""+t).replace(S[n],function(t){return M[n][t]})}}),w.result=function(n,t){if(null==n)return null;var r=n[t];return w.isFunction(r)?r.call(n):r},w.mixin=function(n){A(w.functions(n),function(t){var r=w[t]=n[t];w.prototype[t]=function(){var n=[this._wrapped];return a.apply(n,arguments),D.call(this,r.apply(w,n))}})};var N=0;w.uniqueId=function(n){var t=++N+"";return n?n+t:t},w.templateSettings={evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g,escape:/<%-([\s\S]+?)%>/g};var T=/(.)^/,q={"'":"'","\\":"\\","\r":"r","\n":"n"," ":"t","\u2028":"u2028","\u2029":"u2029"},B=/\\|'|\r|\n|\t|\u2028|\u2029/g;w.template=function(n,t,r){var e;r=w.defaults({},r,w.templateSettings);var u=RegExp([(r.escape||T).source,(r.interpolate||T).source,(r.evaluate||T).source].join("|")+"|$","g"),i=0,a="__p+='";n.replace(u,function(t,r,e,u,o){return a+=n.slice(i,o).replace(B,function(n){return"\\"+q[n]}),r&&(a+="'+\n((__t=("+r+"))==null?'':_.escape(__t))+\n'"),e&&(a+="'+\n((__t=("+e+"))==null?'':__t)+\n'"),u&&(a+="';\n"+u+"\n__p+='"),i=o+t.length,t}),a+="';\n",r.variable||(a="with(obj||{}){\n"+a+"}\n"),a="var __t,__p='',__j=Array.prototype.join,"+"print=function(){__p+=__j.call(arguments,'');};\n"+a+"return __p;\n";try{e=Function(r.variable||"obj","_",a)}catch(o){throw o.source=a,o}if(t)return e(t,w);var c=function(n){return e.call(this,n,w)};return c.source="function("+(r.variable||"obj")+"){\n"+a+"}",c},w.chain=function(n){return w(n).chain()};var D=function(n){return this._chain?w(n).chain():n};w.mixin(w),A(["pop","push","reverse","shift","sort","splice","unshift"],function(n){var t=e[n];w.prototype[n]=function(){var r=this._wrapped;return t.apply(r,arguments),"shift"!=n&&"splice"!=n||0!==r.length||delete r[0],D.call(this,r)}}),A(["concat","join","slice"],function(n){var t=e[n];w.prototype[n]=function(){return D.call(this,t.apply(this._wrapped,arguments))}}),w.extend(w.prototype,{chain:function(){return this._chain=!0,this},value:function(){return this._wrapped}})}).call(this);
\ No newline at end of file
--- a/web/res/metadataplayer/HtmlPlayer.js Wed Apr 10 18:38:21 2013 +0200
+++ b/web/res/metadataplayer/HtmlPlayer.js Fri Jun 07 11:55:46 2013 +0200
@@ -10,118 +10,6 @@
IriSP.Widgets.HtmlPlayer.prototype.draw = function() {
-
- if (typeof this.video === "undefined") {
- this.video = this.media.video;
- }
-
- if (this.url_transform) {
- this.video = this.url_transform(this.video);
- }
-
- var videoEl = IriSP.jQuery('<video>');
- videoEl.attr({
- width : this.width,
- height : this.height || undefined
- });
- if(typeof this.video === "string"){
- videoEl.attr("src",this.video);
- } else {
- for (var i = 0; i < this.video.length; i++) {
- var _srcNode = IriSP.jQuery('<source>');
- _srcNode.attr({
- src: this.video[i].src,
- type: this.video[i].type
- });
- videoEl.append(_srcNode);
- }
- }
- this.$.html(videoEl);
- if (this.autostart || this.autoplay) {
- videoEl.attr("autoplay", true);
- }
-
- var mediaEl = videoEl[0],
- media = this.media;
-
- // Binding functions to Popcorn
- media.on("setcurrenttime", function(_milliseconds) {
- try {
- mediaEl.currentTime = (_milliseconds / 1000);
- } catch (err) {
-
- }
- });
-
- media.on("setvolume", function(_vol) {
- media.volume = _vol;
- try {
- mediaEl.volume = _vol;
- } catch (err) {
-
- }
- });
-
- media.on("setmuted", function(_muted) {
- media.muted = _muted;
- try {
- mediaEl.muted = _muted;
- } catch (err) {
-
- }
- });
-
- media.on("setplay", function() {
- try {
- mediaEl.play();
- } catch (err) {
-
- }
- });
-
- media.on("setpause", function() {
- try {
- mediaEl.pause();
- } catch (err) {
-
- }
- });
-
- // Binding Popcorn events to media
- function getVolume() {
- media.muted = mediaEl.muted;
- media.volume = mediaEl.volume;
- }
-
- videoEl.on("loadedmetadata", function() {
- getVolume();
- media.trigger("loadedmetadata");
- media.trigger("volumechange");
- })
-
- videoEl.on("timeupdate", function() {
- media.trigger("timeupdate", new IriSP.Model.Time(1000*mediaEl.currentTime));
- });
-
- videoEl.on("volumechange", function() {
- getVolume();
- media.trigger("volumechange");
- })
-
- videoEl.on("play", function() {
- media.trigger("play");
- });
-
- videoEl.on("pause", function() {
- media.trigger("pause");
- });
-
- videoEl.on("seeking", function() {
- media.trigger("seeking");
- });
-
- videoEl.on("seeked", function() {
- media.trigger("seeked");
- });
+ IriSP.htmlPlayer(this.media, this.$, this);
}
\ No newline at end of file
--- a/web/res/metadataplayer/LdtPlayer-core.js Wed Apr 10 18:38:21 2013 +0200
+++ b/web/res/metadataplayer/LdtPlayer-core.js Fri Jun 07 11:55:46 2013 +0200
@@ -1,6 +1,18 @@
+/*! LAB.js (LABjs :: Loading And Blocking JavaScript)
+ v2.0.3 (c) Kyle Simpson
+ MIT License
+*/
+(function(o){var K=o.$LAB,y="UseLocalXHR",z="AlwaysPreserveOrder",u="AllowDuplicates",A="CacheBust",B="BasePath",C=/^[^?#]*\//.exec(location.href)[0],D=/^\w+\:\/\/\/?[^\/]+/.exec(C)[0],i=document.head||document.getElementsByTagName("head"),L=(o.opera&&Object.prototype.toString.call(o.opera)=="[object Opera]")||("MozAppearance"in document.documentElement.style),q=document.createElement("script"),E=typeof q.preload=="boolean",r=E||(q.readyState&&q.readyState=="uninitialized"),F=!r&&q.async===true,M=!r&&!F&&!L;function G(a){return Object.prototype.toString.call(a)=="[object Function]"}function H(a){return Object.prototype.toString.call(a)=="[object Array]"}function N(a,c){var b=/^\w+\:\/\//;if(/^\/\/\/?/.test(a)){a=location.protocol+a}else if(!b.test(a)&&a.charAt(0)!="/"){a=(c||"")+a}return b.test(a)?a:((a.charAt(0)=="/"?D:C)+a)}function s(a,c){for(var b in a){if(a.hasOwnProperty(b)){c[b]=a[b]}}return c}function O(a){var c=false;for(var b=0;b<a.scripts.length;b++){if(a.scripts[b].ready&&a.scripts[b].exec_trigger){c=true;a.scripts[b].exec_trigger();a.scripts[b].exec_trigger=null}}return c}function t(a,c,b,d){a.onload=a.onreadystatechange=function(){if((a.readyState&&a.readyState!="complete"&&a.readyState!="loaded")||c[b])return;a.onload=a.onreadystatechange=null;d()}}function I(a){a.ready=a.finished=true;for(var c=0;c<a.finished_listeners.length;c++){a.finished_listeners[c]()}a.ready_listeners=[];a.finished_listeners=[]}function P(d,f,e,g,h){setTimeout(function(){var a,c=f.real_src,b;if("item"in i){if(!i[0]){setTimeout(arguments.callee,25);return}i=i[0]}a=document.createElement("script");if(f.type)a.type=f.type;if(f.charset)a.charset=f.charset;if(h){if(r){e.elem=a;if(E){a.preload=true;a.onpreload=g}else{a.onreadystatechange=function(){if(a.readyState=="loaded")g()}}a.src=c}else if(h&&c.indexOf(D)==0&&d[y]){b=new XMLHttpRequest();b.onreadystatechange=function(){if(b.readyState==4){b.onreadystatechange=function(){};e.text=b.responseText+"\n//@ sourceURL="+c;g()}};b.open("GET",c);b.send()}else{a.type="text/cache-script";t(a,e,"ready",function(){i.removeChild(a);g()});a.src=c;i.insertBefore(a,i.firstChild)}}else if(F){a.async=false;t(a,e,"finished",g);a.src=c;i.insertBefore(a,i.firstChild)}else{t(a,e,"finished",g);a.src=c;i.insertBefore(a,i.firstChild)}},0)}function J(){var l={},Q=r||M,n=[],p={},m;l[y]=true;l[z]=false;l[u]=false;l[A]=false;l[B]="";function R(a,c,b){var d;function f(){if(d!=null){d=null;I(b)}}if(p[c.src].finished)return;if(!a[u])p[c.src].finished=true;d=b.elem||document.createElement("script");if(c.type)d.type=c.type;if(c.charset)d.charset=c.charset;t(d,b,"finished",f);if(b.elem){b.elem=null}else if(b.text){d.onload=d.onreadystatechange=null;d.text=b.text}else{d.src=c.real_src}i.insertBefore(d,i.firstChild);if(b.text){f()}}function S(c,b,d,f){var e,g,h=function(){b.ready_cb(b,function(){R(c,b,e)})},j=function(){b.finished_cb(b,d)};b.src=N(b.src,c[B]);b.real_src=b.src+(c[A]?((/\?.*$/.test(b.src)?"&_":"?_")+~~(Math.random()*1E9)+"="):"");if(!p[b.src])p[b.src]={items:[],finished:false};g=p[b.src].items;if(c[u]||g.length==0){e=g[g.length]={ready:false,finished:false,ready_listeners:[h],finished_listeners:[j]};P(c,b,e,((f)?function(){e.ready=true;for(var a=0;a<e.ready_listeners.length;a++){e.ready_listeners[a]()}e.ready_listeners=[]}:function(){I(e)}),f)}else{e=g[0];if(e.finished){j()}else{e.finished_listeners.push(j)}}}function v(){var e,g=s(l,{}),h=[],j=0,w=false,k;function T(a,c){a.ready=true;a.exec_trigger=c;x()}function U(a,c){a.ready=a.finished=true;a.exec_trigger=null;for(var b=0;b<c.scripts.length;b++){if(!c.scripts[b].finished)return}c.finished=true;x()}function x(){while(j<h.length){if(G(h[j])){try{h[j++]()}catch(err){}continue}else if(!h[j].finished){if(O(h[j]))continue;break}j++}if(j==h.length){w=false;k=false}}function V(){if(!k||!k.scripts){h.push(k={scripts:[],finished:true})}}e={script:function(){for(var f=0;f<arguments.length;f++){(function(a,c){var b;if(!H(a)){c=[a]}for(var d=0;d<c.length;d++){V();a=c[d];if(G(a))a=a();if(!a)continue;if(H(a)){b=[].slice.call(a);b.unshift(d,1);[].splice.apply(c,b);d--;continue}if(typeof a=="string")a={src:a};a=s(a,{ready:false,ready_cb:T,finished:false,finished_cb:U});k.finished=false;k.scripts.push(a);S(g,a,k,(Q&&w));w=true;if(g[z])e.wait()}})(arguments[f],arguments[f])}return e},wait:function(){if(arguments.length>0){for(var a=0;a<arguments.length;a++){h.push(arguments[a])}k=h[h.length-1]}else k=false;x();return e}};return{script:e.script,wait:e.wait,setOptions:function(a){s(a,g);return e}}}m={setGlobalDefaults:function(a){s(a,l);return m},setOptions:function(){return v().setOptions.apply(null,arguments)},script:function(){return v().script.apply(null,arguments)},wait:function(){return v().wait.apply(null,arguments)},queueScript:function(){n[n.length]={type:"script",args:[].slice.call(arguments)};return m},queueWait:function(){n[n.length]={type:"wait",args:[].slice.call(arguments)};return m},runQueue:function(){var a=m,c=n.length,b=c,d;for(;--b>=0;){d=n.shift();a=a[d.type].apply(null,d.args)}return a},noConflict:function(){o.$LAB=K;return m},sandbox:function(){return J()}};return m}o.$LAB=J();(function(a,c,b){if(document.readyState==null&&document[a]){document.readyState="loading";document[a](c,b=function(){document.removeEventListener(c,b,false);document.readyState="complete"},false)}})("addEventListener","DOMContentLoaded")})(this);
/*
- *
- * Copyright 2010-2012 Institut de recherche et d'innovation
+ *
+ __ __ _ _ _ _
+ | \/ | ___| |_ __ _ __| | __ _| |_ __ _ _ __ | | __ _ _ _ ___ _ __
+ | |\/| |/ _ \ __/ _` |/ _` |/ _` | __/ _` | '_ \| |/ _` | | | |/ _ \ '__|
+ | | | | __/ || (_| | (_| | (_| | || (_| | |_) | | (_| | |_| | __/ |
+ |_| |_|\___|\__\__,_|\__,_|\__,_|\__\__,_| .__/|_|\__,_|\__, |\___|_|
+ |_| |___/
+
+ * Copyright 2010-2012 Institut de recherche et d'innovation
* contributor(s) : Karim Hamidou, Samuel Huron, Raphael Velt, Thibaut Cavalie
*
* contact@iri.centrepompidou.fr
@@ -16,210 +28,24 @@
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL-C license and that you accept its terms.
*/
-/*! LAB.js (LABjs :: Loading And Blocking JavaScript)
- v2.0.3 (c) Kyle Simpson
- MIT License
-*/
-(function(o){var K=o.$LAB,y="UseLocalXHR",z="AlwaysPreserveOrder",u="AllowDuplicates",A="CacheBust",B="BasePath",C=/^[^?#]*\//.exec(location.href)[0],D=/^\w+\:\/\/\/?[^\/]+/.exec(C)[0],i=document.head||document.getElementsByTagName("head"),L=(o.opera&&Object.prototype.toString.call(o.opera)=="[object Opera]")||("MozAppearance"in document.documentElement.style),q=document.createElement("script"),E=typeof q.preload=="boolean",r=E||(q.readyState&&q.readyState=="uninitialized"),F=!r&&q.async===true,M=!r&&!F&&!L;function G(a){return Object.prototype.toString.call(a)=="[object Function]"}function H(a){return Object.prototype.toString.call(a)=="[object Array]"}function N(a,c){var b=/^\w+\:\/\//;if(/^\/\/\/?/.test(a)){a=location.protocol+a}else if(!b.test(a)&&a.charAt(0)!="/"){a=(c||"")+a}return b.test(a)?a:((a.charAt(0)=="/"?D:C)+a)}function s(a,c){for(var b in a){if(a.hasOwnProperty(b)){c[b]=a[b]}}return c}function O(a){var c=false;for(var b=0;b<a.scripts.length;b++){if(a.scripts[b].ready&&a.scripts[b].exec_trigger){c=true;a.scripts[b].exec_trigger();a.scripts[b].exec_trigger=null}}return c}function t(a,c,b,d){a.onload=a.onreadystatechange=function(){if((a.readyState&&a.readyState!="complete"&&a.readyState!="loaded")||c[b])return;a.onload=a.onreadystatechange=null;d()}}function I(a){a.ready=a.finished=true;for(var c=0;c<a.finished_listeners.length;c++){a.finished_listeners[c]()}a.ready_listeners=[];a.finished_listeners=[]}function P(d,f,e,g,h){setTimeout(function(){var a,c=f.real_src,b;if("item"in i){if(!i[0]){setTimeout(arguments.callee,25);return}i=i[0]}a=document.createElement("script");if(f.type)a.type=f.type;if(f.charset)a.charset=f.charset;if(h){if(r){e.elem=a;if(E){a.preload=true;a.onpreload=g}else{a.onreadystatechange=function(){if(a.readyState=="loaded")g()}}a.src=c}else if(h&&c.indexOf(D)==0&&d[y]){b=new XMLHttpRequest();b.onreadystatechange=function(){if(b.readyState==4){b.onreadystatechange=function(){};e.text=b.responseText+"\n//@ sourceURL="+c;g()}};b.open("GET",c);b.send()}else{a.type="text/cache-script";t(a,e,"ready",function(){i.removeChild(a);g()});a.src=c;i.insertBefore(a,i.firstChild)}}else if(F){a.async=false;t(a,e,"finished",g);a.src=c;i.insertBefore(a,i.firstChild)}else{t(a,e,"finished",g);a.src=c;i.insertBefore(a,i.firstChild)}},0)}function J(){var l={},Q=r||M,n=[],p={},m;l[y]=true;l[z]=false;l[u]=false;l[A]=false;l[B]="";function R(a,c,b){var d;function f(){if(d!=null){d=null;I(b)}}if(p[c.src].finished)return;if(!a[u])p[c.src].finished=true;d=b.elem||document.createElement("script");if(c.type)d.type=c.type;if(c.charset)d.charset=c.charset;t(d,b,"finished",f);if(b.elem){b.elem=null}else if(b.text){d.onload=d.onreadystatechange=null;d.text=b.text}else{d.src=c.real_src}i.insertBefore(d,i.firstChild);if(b.text){f()}}function S(c,b,d,f){var e,g,h=function(){b.ready_cb(b,function(){R(c,b,e)})},j=function(){b.finished_cb(b,d)};b.src=N(b.src,c[B]);b.real_src=b.src+(c[A]?((/\?.*$/.test(b.src)?"&_":"?_")+~~(Math.random()*1E9)+"="):"");if(!p[b.src])p[b.src]={items:[],finished:false};g=p[b.src].items;if(c[u]||g.length==0){e=g[g.length]={ready:false,finished:false,ready_listeners:[h],finished_listeners:[j]};P(c,b,e,((f)?function(){e.ready=true;for(var a=0;a<e.ready_listeners.length;a++){e.ready_listeners[a]()}e.ready_listeners=[]}:function(){I(e)}),f)}else{e=g[0];if(e.finished){j()}else{e.finished_listeners.push(j)}}}function v(){var e,g=s(l,{}),h=[],j=0,w=false,k;function T(a,c){a.ready=true;a.exec_trigger=c;x()}function U(a,c){a.ready=a.finished=true;a.exec_trigger=null;for(var b=0;b<c.scripts.length;b++){if(!c.scripts[b].finished)return}c.finished=true;x()}function x(){while(j<h.length){if(G(h[j])){try{h[j++]()}catch(err){}continue}else if(!h[j].finished){if(O(h[j]))continue;break}j++}if(j==h.length){w=false;k=false}}function V(){if(!k||!k.scripts){h.push(k={scripts:[],finished:true})}}e={script:function(){for(var f=0;f<arguments.length;f++){(function(a,c){var b;if(!H(a)){c=[a]}for(var d=0;d<c.length;d++){V();a=c[d];if(G(a))a=a();if(!a)continue;if(H(a)){b=[].slice.call(a);b.unshift(d,1);[].splice.apply(c,b);d--;continue}if(typeof a=="string")a={src:a};a=s(a,{ready:false,ready_cb:T,finished:false,finished_cb:U});k.finished=false;k.scripts.push(a);S(g,a,k,(Q&&w));w=true;if(g[z])e.wait()}})(arguments[f],arguments[f])}return e},wait:function(){if(arguments.length>0){for(var a=0;a<arguments.length;a++){h.push(arguments[a])}k=h[h.length-1]}else k=false;x();return e}};return{script:e.script,wait:e.wait,setOptions:function(a){s(a,g);return e}}}m={setGlobalDefaults:function(a){s(a,l);return m},setOptions:function(){return v().setOptions.apply(null,arguments)},script:function(){return v().script.apply(null,arguments)},wait:function(){return v().wait.apply(null,arguments)},queueScript:function(){n[n.length]={type:"script",args:[].slice.call(arguments)};return m},queueWait:function(){n[n.length]={type:"wait",args:[].slice.call(arguments)};return m},runQueue:function(){var a=m,c=n.length,b=c,d;for(;--b>=0;){d=n.shift();a=a[d.type].apply(null,d.args)}return a},noConflict:function(){o.$LAB=K;return m},sandbox:function(){return J()}};return m}o.$LAB=J();(function(a,c,b){if(document.readyState==null&&document[a]){document.readyState="loading";document[a](c,b=function(){document.removeEventListener(c,b,false);document.readyState="complete"},false)}})("addEventListener","DOMContentLoaded")})(this);/* init.js - initialization and configuration of the widgets
-*/
+/* Initialization of the namespace */
if (typeof window.IriSP === "undefined") {
window.IriSP = {};
}
-/* The Metadataplayer Object, single point of entry, replaces IriSP.init_player */
-
-IriSP.Metadataplayer = function(config) {
- IriSP.log("IriSP.Metadataplayer constructor");
- for (var key in IriSP.guiDefaults) {
- if (IriSP.guiDefaults.hasOwnProperty(key) && !config.hasOwnProperty(key)) {
- config[key] = IriSP.guiDefaults[key]
- }
- }
- var _container = document.getElementById(config.container);
- _container.innerHTML = '<h3 class="Ldt-Loader">Loading... Chargement...</h3>';
- this.sourceManager = new IriSP.Model.Directory();
- this.config = config;
- this.__events = {};
- this.loadLibs();
-}
-
-IriSP.Metadataplayer.prototype.toString = function() {
- return 'Metadataplayer in #' + this.config.container;
-}
-
-IriSP.Metadataplayer.prototype.on = function(_event, _callback) {
- if (typeof this.__events[_event] === "undefined") {
- this.__events[_event] = [];
- }
- this.__events[_event].push(_callback);
-}
-
-IriSP.Metadataplayer.prototype.trigger = function(_event, _data) {
- var _element = this;
- IriSP._(this.__events[_event]).each(function(_callback) {
- _callback.call(_element, _data);
- });
-}
-
-IriSP.Metadataplayer.prototype.loadLibs = function() {
- IriSP.log("IriSP.Metadataplayer.prototype.loadLibs");
- var $L = $LAB
- .script(IriSP.getLib("underscore"))
- .script(IriSP.getLib("Mustache"))
- .script(IriSP.getLib("jQuery"));
-
- if (typeof JSON == "undefined") {
- $L.script(IriSP.getLib("json"));
- }
-
- $L.wait()
- .script(IriSP.getLib("jQueryUI"));
-
- /* widget specific requirements */
- for(var _i = 0; _i < this.config.widgets.length; _i++) {
- var _t = this.config.widgets[_i].type;
- if (typeof IriSP.widgetsRequirements[_t] !== "undefined" && typeof IriSP.widgetsRequirements[_t].requires !== "undefined" ) {
- for (var _j = 0; _j < IriSP.widgetsRequirements[_t].requires.length; _j++) {
- $L.script(IriSP.getLib(IriSP.widgetsRequirements[_t].requires[_j]));
- }
- }
- }
-
- var _this = this;
-
- $L.wait(function() {
- _this.onLibsLoaded();
- });
+if (typeof IriSP.jQuery === "undefined" && typeof window.jQuery !== "undefined" && parseFloat(window.jQuery().jquery) >= 1.7) {
+ IriSP.jQuery = window.jQuery;
}
-IriSP.Metadataplayer.prototype.onLibsLoaded = function() {
- IriSP.log("IriSP.Metadataplayer.prototype.onLibsLoaded");
- if (typeof IriSP.jQuery === "undefined" && typeof window.jQuery !== "undefined") {
- IriSP.jQuery = window.jQuery;
- }
- if (typeof IriSP._ === "undefined" && typeof window._ !== "undefined") {
- IriSP._ = window._;
- }
- IriSP.loadCss(IriSP.getLib("cssjQueryUI"));
- IriSP.loadCss(this.config.css);
-
- this.$ = IriSP.jQuery('#' + this.config.container);
- this.$.css({
- "width": this.config.width,
- "clear": "both"
- });
- if (typeof this.config.height !== "undefined") {
- this.$.css("height", this.config.height);
- }
-
- this.widgets = [];
- var _this = this;
- IriSP._(this.config.widgets).each(function(widgetconf, key) {
- _this.widgets.push(null);
- _this.loadWidget(widgetconf, function(widget) {
- _this.widgets[key] = widget;
- if (widget.isLoaded()) {
- _this.trigger("widget-loaded");
- }
- });
- });
- this.$.find('.Ldt-Loader').detach();
-
- this.widgetsLoaded = false;
-
- this.on("widget-loaded", function() {
- if (_this.widgetsLoaded) {
- return;
- }
- var isloaded = !IriSP._(_this.widgets).any(function(w) {
- return !(w && w.isLoaded())
- });
- if (isloaded) {
- _this.widgetsLoaded = true;
- _this.trigger("widgets-loaded");
- }
- });
+if (typeof IriSP._ === "undefined" && typeof window._ !== "undefined" && parseFloat(window._.VERSION) >= 1.4) {
+ IriSP._ = window._;
}
-
-IriSP.Metadataplayer.prototype.loadMetadata = function(_metadataInfo) {
- if (_metadataInfo.elementType === "source") {
- return _metadataInfo;
- }
- if (typeof _metadataInfo.serializer === "undefined" && typeof _metadataInfo.format !== "undefined") {
- _metadataInfo.serializer = IriSP.serializers[_metadataInfo.format];
- }
- if (typeof _metadataInfo.url !== "undefined" && typeof _metadataInfo.serializer !== "undefined") {
- return this.sourceManager.remoteSource(_metadataInfo);
- } else {
- return this.sourceManager.newLocalSource(_metadataInfo);
- }
-}
-
-IriSP.Metadataplayer.prototype.loadWidget = function(_widgetConfig, _callback) {
- /* Creating containers if needed */
- if (typeof _widgetConfig.container === "undefined") {
- var _divs = this.layoutDivs(_widgetConfig.type);
- _widgetConfig.container = _divs[0];
- }
-
- var _this = this;
-
- if (typeof IriSP.Widgets[_widgetConfig.type] !== "undefined") {
- IriSP._.defer(function() {
- _callback(new IriSP.Widgets[_widgetConfig.type](_this, _widgetConfig));
- });
- } else {
- /* Loading Widget CSS */
- if (typeof IriSP.widgetsRequirements[_widgetConfig.type] === "undefined" || typeof IriSP.widgetsRequirements[_widgetConfig.type].noCss === "undefined" || !IriSP.widgetsRequirements[_widgetConfig.type].noCss) {
- IriSP.loadCss(IriSP.widgetsDir + '/' + _widgetConfig.type + '.css');
- }
- /* Loading Widget JS */
- $LAB.script(IriSP.widgetsDir + '/' + _widgetConfig.type + '.js').wait(function() {
- _callback(new IriSP.Widgets[_widgetConfig.type](_this, _widgetConfig));
- });
- }
-}
-
-/** create a subdiv with an unique id, and a spacer div as well.
- @param widgetName the name of the widget.
- @return an array of the form [createdivId, spacerdivId].
-*/
-IriSP.Metadataplayer.prototype.layoutDivs = function(_name, _height) {
- if (typeof(_name) === "undefined") {
- _name = "";
- }
- var newDiv = IriSP._.uniqueId(this.config.container + "_widget_" + _name + "_"),
- spacerDiv = IriSP._.uniqueId("LdtPlayer_spacer_"),
- divHtml = IriSP.jQuery('<div>')
- .attr("id",newDiv)
- .css({
- width: this.config.width + "px",
- position: "relative",
- clear: "both"
- }),
- spacerHtml = IriSP.jQuery('<div>')
- .attr("id",spacerDiv)
- .css({
- width: this.config.width + "px",
- height: this.config.spacer_div_height + "px",
- position: "relative",
- clear: "both"
- });
- if (typeof _height !== "undefined") {
- divHtml.css("height", _height);
- }
-
- this.$.append(divHtml);
- this.$.append(spacerHtml);
-
- return [newDiv, spacerDiv];
-};
/* utils.js - various utils that don't belong anywhere else */
IriSP.jqEscape = function(_text) {
return _text.replace(/(:|\.)/g,'\\$1');
-}
+};
IriSP.getLib = function(lib) {
if (IriSP.libFiles.useCdn && typeof IriSP.libFiles.cdn[lib] == "string") {
@@ -244,7 +70,7 @@
}).appendTo('head');
IriSP._cssCache.push(_cssFile);
}
-}
+};
IriSP.textFieldHtml = function(_text, _regexp, _extend) {
var list = [],
@@ -317,13 +143,13 @@
return res;
-}
+};
IriSP.log = function() {
if (typeof console !== "undefined" && typeof IriSP.logging !== "undefined" && IriSP.logging) {
console.log.apply(console, arguments);
}
-}
+};
IriSP.attachDndData = function(jqSel, data) {
jqSel.attr("draggable", "true").on("dragstart", function(_event) {
@@ -338,10 +164,12 @@
_event.originalEvent.dataTransfer.setData("Text", JSON.stringify(d));
}
});
-}
-/* TODO: Separate Project-specific data from Source */
+};
-/* model.js is where data is stored in a standard form, whatever the serializer */
+/* js is where data is stored in a standard form, whatever the serializer */
+
+//TODO: Separate Project-specific data from Source
+
IriSP.Model = (function (ns) {
function pad(n, x, b) {
@@ -372,23 +200,32 @@
String.fromCharCode(768), String.fromCharCode(769), String.fromCharCode(770), String.fromCharCode(771), String.fromCharCode(807),
"{", "}", "(", ")", "[", "]", "【", "】", "、", "・", "‥", "。", "「", "」", "『", "』", "〜", ":", "!", "?", " ",
",", " ", ";", "(", ")", ".", "*", "+", "\\", "?", "|", "{", "}", "[", "]", "^", "#", "/"
- ]
+ ];
-var Model = {
- _SOURCE_STATUS_EMPTY : 0,
- _SOURCE_STATUS_WAITING : 1,
- _SOURCE_STATUS_READY : 2,
- getUID : function() {
+var Model = {},
+ _SOURCE_STATUS_EMPTY = Model._SOURCE_STATUS_EMPTY = 0,
+ _SOURCE_STATUS_WAITING = Model._SOURCE_STATUS_WAITING = 1,
+ _SOURCE_STATUS_READY = Model._SOURCE_STATUS_READY = 2,
+ extendPrototype = Model.extendPrototype = function(toClass, fromClass) {
+ var fromP = fromClass.prototype,
+ toP = toClass.prototype;
+ for (var k in fromP) {
+ if (fromP.hasOwnProperty(k)) {
+ toP[k] = fromP[k];
+ }
+ }
+ },
+ getUID = Model.getUID = function() {
return uidbase + pad(4, (++uidincrement % 0x10000), 16) + "-" + rand16(4) + "-" + rand16(6) + rand16(6);
},
- isLocalURL : function(url) {
+ isLocalURL = Model.isLocalURL = function(url) {
var matches = url.match(/^(\w+:)\/\/([^/]+)/);
if (matches) {
return(matches[1] === document.location.protocol && matches[2] === document.location.host)
}
return true;
},
- regexpFromTextOrArray : function(_textOrArray, _testOnly, _iexact) {
+ regexpFromTextOrArray = Model.regexpFromTextOrArray = function(_textOrArray, _testOnly, _iexact) {
var _testOnly = _testOnly || false,
_iexact = _iexact || false;
function escapeText(_text) {
@@ -408,7 +245,7 @@
}
return new RegExp( _source, _flags);
},
- fullTextRegexps: function(_text) {
+ fullTextRegexps = Model.fullTextRegexps = function(_text) {
var remsrc = "[\\" + removeChars.join("\\") + "]",
remrx = new RegExp(remsrc,"gm"),
txt = _text.toLowerCase().replace(remrx,"")
@@ -429,7 +266,7 @@
}
return "(" + src + ")";
},
- isoToDate : function(_str) {
+ isoToDate = Model.isoToDate = function(_str) {
// http://delete.me.uk/2005/03/iso8601.html
var regexp = "([0-9]{4})(-([0-9]{2})(-([0-9]{2})(T([0-9]{2}):([0-9]{2})(:([0-9]{2})(\.([0-9]+))?)?(Z|(([-+])([0-9]{2}):([0-9]{2})))?)?)?)?";
var d = _str.match(new RegExp(regexp));
@@ -454,7 +291,7 @@
_res.setTime(Number(time));
return _res;
},
- dateToIso : function(_d) {
+ dateToIso = Model.dateToIso = function(_d) {
var d = _d ? new Date(_d) : new Date();
return d.getUTCFullYear()+'-'
+ pad(2, d.getUTCMonth()+1)+'-'
@@ -462,20 +299,19 @@
+ pad(2, d.getUTCHours())+':'
+ pad(2, d.getUTCMinutes())+':'
+ pad(2, d.getUTCSeconds())+'Z'
- }
-}
+ };
/*
- * Model.List is a class for a list of elements (e.g. annotations, medias, etc. that each have a distinct ID)
+ * List is a class for a list of elements (e.g. annotations, medias, etc. that each have a distinct ID)
*/
-Model.List = function(_directory) {
+var List = Model.List = function(_directory) {
Array.call(this);
this.directory = _directory;
this.idIndex = [];
this.__events = {};
if (typeof _directory == "undefined") {
console.trace();
- throw "Error : new Model.List(directory): directory is undefined";
+ throw "Error : new List(directory): directory is undefined";
}
var _this = this;
this.on("clear-search", function() {
@@ -486,113 +322,113 @@
});
_this.trigger("search-cleared");
})
-}
+};
-Model.List.prototype = new Array();
+List.prototype = new Array();
-Model.List.prototype.hasId = function(_id) {
+List.prototype.hasId = function(_id) {
return ns._(this.idIndex).include(_id);
-}
+};
/* On recent browsers, forEach and map are defined and do what we want.
* Otherwise, we'll use the Underscore.js functions
*/
if (typeof Array.prototype.forEach === "undefined") {
- Model.List.prototype.forEach = function(_callback) {
+ List.prototype.forEach = function(_callback) {
var _this = this;
ns._(this).forEach(function(_value, _key) {
_callback(_value, _key, _this);
});
}
-}
+};
if (typeof Array.prototype.map === "undefined") {
- Model.List.prototype.map = function(_callback) {
+ List.prototype.map = function(_callback) {
var _this = this;
return ns._(this).map(function(_value, _key) {
return _callback(_value, _key, _this);
});
}
-}
+};
-Model.List.prototype.pluck = function(_key) {
+List.prototype.pluck = function(_key) {
return this.map(function(_value) {
return _value[_key];
});
-}
+};
-/* We override Array's filter function because it doesn't return an Model.List
+/* We override Array's filter function because it doesn't return an List
*/
-Model.List.prototype.filter = function(_callback) {
+List.prototype.filter = function(_callback) {
var _this = this,
- _res = new Model.List(this.directory);
+ _res = new List(this.directory);
_res.addElements(ns._(this).filter(function(_value, _key) {
return _callback(_value, _key, _this);
}));
return _res;
-}
+};
-Model.List.prototype.slice = function(_start, _end) {
- var _res = new Model.List(this.directory);
+List.prototype.slice = function(_start, _end) {
+ var _res = new List(this.directory);
_res.addElements(Array.prototype.slice.call(this, _start, _end));
return _res;
-}
+};
-Model.List.prototype.splice = function(_start, _end) {
- var _res = new Model.List(this.directory);
+List.prototype.splice = function(_start, _end) {
+ var _res = new List(this.directory);
_res.addElements(Array.prototype.splice.call(this, _start, _end));
this.idIndex.splice(_start, _end);
return _res;
-}
+};
/* Array has a sort function, but it's not as interesting as Underscore.js's sortBy
- * and won't return a new Model.List
+ * and won't return a new List
*/
-Model.List.prototype.sortBy = function(_callback) {
+List.prototype.sortBy = function(_callback) {
var _this = this,
- _res = new Model.List(this.directory);
+ _res = new List(this.directory);
_res.addElements(ns._(this).sortBy(function(_value, _key) {
return _callback(_value, _key, _this);
}));
return _res;
-}
+};
/* Title and Description are basic information for (almost) all element types,
* here we can search by these criteria
*/
-Model.List.prototype.searchByTitle = function(_text, _iexact) {
+List.prototype.searchByTitle = function(_text, _iexact) {
var _iexact = _iexact || false,
- _rgxp = Model.regexpFromTextOrArray(_text, true, _iexact);
+ _rgxp = regexpFromTextOrArray(_text, true, _iexact);
return this.filter(function(_element) {
return _rgxp.test(_element.title);
});
-}
+};
-Model.List.prototype.searchByDescription = function(_text, _iexact) {
+List.prototype.searchByDescription = function(_text, _iexact) {
var _iexact = _iexact || false,
- _rgxp = Model.regexpFromTextOrArray(_text, true, _iexact);
+ _rgxp = regexpFromTextOrArray(_text, true, _iexact);
return this.filter(function(_element) {
return _rgxp.test(_element.description);
});
-}
+};
-Model.List.prototype.searchByTextFields = function(_text, _iexact) {
+List.prototype.searchByTextFields = function(_text, _iexact) {
var _iexact = _iexact || false,
- _rgxp = Model.regexpFromTextOrArray(_text, true, _iexact);
+ _rgxp = regexpFromTextOrArray(_text, true, _iexact);
return this.filter(function(_element) {
var keywords = (_element.keywords || _element.getTagTexts() || []).join(", ");
return _rgxp.test(_element.description) || _rgxp.test(_element.title) || _rgxp.test(keywords);
});
-}
+};
-Model.List.prototype.search = function(_text) {
+List.prototype.search = function(_text) {
if (!_text) {
this.trigger("clear-search");
return this;
}
this.searching = true;
this.trigger("search", _text);
- var rxsource = Model.fullTextRegexps(_text)
+ var rxsource = fullTextRegexps(_text)
rgxp = new RegExp(rxsource,"im"),
this.regexp = new RegExp(rxsource,"gim");
var res = this.filter(function(_element, _k) {
@@ -605,23 +441,23 @@
});
this.trigger(res.length ? "found" : "not-found",res);
return res;
-}
+};
-Model.List.prototype.getTitles = function() {
+List.prototype.getTitles = function() {
return this.map(function(_el) {
return _el.title;
});
-}
+};
-Model.List.prototype.addId = function(_id) {
+List.prototype.addId = function(_id) {
var _el = this.directory.getElement(_id)
if (!this.hasId(_id) && typeof _el !== "undefined") {
this.idIndex.push(_id);
Array.prototype.push.call(this, _el);
}
-}
+};
-Model.List.prototype.push = function(_el) {
+List.prototype.push = function(_el) {
if (typeof _el === "undefined") {
return;
}
@@ -632,24 +468,24 @@
} else {
this[_index] = _el;
}
-}
+};
-Model.List.prototype.addIds = function(_array) {
+List.prototype.addIds = function(_array) {
var _l = _array.length,
_this = this;
ns._(_array).forEach(function(_id) {
_this.addId(_id);
});
-}
+};
-Model.List.prototype.addElements = function(_array) {
+List.prototype.addElements = function(_array) {
var _this = this;
ns._(_array).forEach(function(_el) {
_this.push(_el);
});
-}
+};
-Model.List.prototype.removeId = function(_id, _deleteFromDirectory) {
+List.prototype.removeId = function(_id, _deleteFromDirectory) {
var _deleteFromDirectory = _deleteFromDirectory || false,
_index = (ns._(this.idIndex).indexOf(_id));
if (_index !== -1) {
@@ -658,61 +494,61 @@
if (_deleteFromDirectory) {
delete this.directory.elements[_id];
}
-}
+};
-Model.List.prototype.removeElement = function(_el, _deleteFromDirectory) {
+List.prototype.removeElement = function(_el, _deleteFromDirectory) {
var _deleteFromDirectory = _deleteFromDirectory || false;
this.removeId(_el.id);
-}
+};
-Model.List.prototype.removeIds = function(_list, _deleteFromDirectory) {
+List.prototype.removeIds = function(_list, _deleteFromDirectory) {
var _deleteFromDirectory = _deleteFromDirectory || false,
_this = this;
ns._(_list).forEach(function(_id) {
_this.removeId(_id);
});
-}
+};
-Model.List.prototype.removeElements = function(_list, _deleteFromDirectory) {
+List.prototype.removeElements = function(_list, _deleteFromDirectory) {
var _deleteFromDirectory = _deleteFromDirectory || false,
_this = this;
ns._(_list).forEach(function(_el) {
_this.removeElement(_el);
});
-}
+};
-Model.List.prototype.on = function(_event, _callback) {
+List.prototype.on = function(_event, _callback) {
if (typeof this.__events[_event] === "undefined") {
this.__events[_event] = [];
}
this.__events[_event].push(_callback);
-}
+};
-Model.List.prototype.off = function(_event, _callback) {
+List.prototype.off = function(_event, _callback) {
if (typeof this.__events[_event] !== "undefined") {
this.__events[_event] = ns._(this.__events[_event]).reject(function(_fn) {
return _fn === _callback;
});
}
-}
+};
-Model.List.prototype.trigger = function(_event, _data) {
+List.prototype.trigger = function(_event, _data) {
var _list = this;
ns._(this.__events[_event]).each(function(_callback) {
_callback.call(_list, _data);
});
-}
+};
/* A simple time management object, that helps converting millisecs to seconds and strings,
* without the clumsiness of the original Date object.
*/
-Model.Time = function(_milliseconds) {
+var Time = Model.Time = function(_milliseconds) {
this.milliseconds = 0;
this.setMilliseconds(_milliseconds);
-}
+};
-Model.Time.prototype.setMilliseconds = function(_milliseconds) {
+Time.prototype.setMilliseconds = function(_milliseconds) {
var _ante = this.milliseconds;
switch(typeof _milliseconds) {
case "string":
@@ -730,17 +566,17 @@
if (this.milliseconds === NaN) {
this.milliseconds = _ante;
}
-}
+};
-Model.Time.prototype.setSeconds = function(_seconds) {
+Time.prototype.setSeconds = function(_seconds) {
this.milliseconds = 1000 * _seconds;
-}
+};
-Model.Time.prototype.getSeconds = function() {
+Time.prototype.getSeconds = function() {
return this.milliseconds / 1000;
-}
+};
-Model.Time.prototype.getHMS = function() {
+Time.prototype.getHMS = function() {
var _totalSeconds = Math.abs(Math.floor(this.getSeconds()));
return {
hours : Math.floor(_totalSeconds / 3600),
@@ -748,17 +584,17 @@
seconds : _totalSeconds % 60,
milliseconds: this.milliseconds % 1000
}
-}
+};
-Model.Time.prototype.add = function(_milliseconds) {
- this.milliseconds += new Model.Time(_milliseconds).milliseconds;
-}
+Time.prototype.add = function(_milliseconds) {
+ this.milliseconds += new Time(_milliseconds).milliseconds;
+};
-Model.Time.prototype.valueOf = function() {
+Time.prototype.valueOf = function() {
return this.milliseconds;
-}
+};
-Model.Time.prototype.toString = function(showCs) {
+Time.prototype.toString = function(showCs) {
var _hms = this.getHMS(),
_res = '';
if (_hms.hours) {
@@ -769,12 +605,12 @@
_res += "." + Math.floor(_hms.milliseconds / 100)
}
return _res;
-}
+};
-/* Model.Reference handles references between elements
+/* Reference handles references between elements
*/
-Model.Reference = function(_source, _idRef) {
+var Reference = Model.Reference = function(_source, _idRef) {
this.source = _source;
this.id = _idRef;
if (typeof _idRef === "object") {
@@ -783,36 +619,36 @@
this.isList = false;
}
this.refresh();
-}
+};
-Model.Reference.prototype.refresh = function() {
+Reference.prototype.refresh = function() {
if (this.isList) {
- this.contents = new Model.List(this.source.directory);
+ this.contents = new List(this.source.directory);
this.contents.addIds(this.id);
} else {
this.contents = this.source.getElement(this.id);
}
-}
+};
-Model.Reference.prototype.getContents = function() {
+Reference.prototype.getContents = function() {
if (typeof this.contents === "undefined" || (this.isList && this.contents.length != this.id.length)) {
this.refresh();
}
return this.contents;
-}
+};
-Model.Reference.prototype.isOrHasId = function(_idRef) {
+Reference.prototype.isOrHasId = function(_idRef) {
if (this.isList) {
return (ns._(this.id).indexOf(_idRef) !== -1)
} else {
return (this.id == _idRef);
}
-}
+};
/* */
-Model.Element = function(_id, _source) {
+var BaseElement = Model.Element = function(_id, _source) {
this.elementType = 'element';
this.title = "";
this.description = "";
@@ -821,69 +657,69 @@
return;
}
if (typeof _id === "undefined" || !_id) {
- _id = Model.getUID();
+ _id = getUID();
}
this.id = _id;
this.source = _source;
if (_source !== this) {
this.source.directory.addElement(this);
}
-}
+};
-Model.Element.prototype.toString = function() {
+BaseElement.prototype.toString = function() {
return this.elementType + (this.elementType !== 'element' ? ', id=' + this.id + ', title="' + this.title + '"' : '');
-}
+};
-Model.Element.prototype.setReference = function(_elementType, _idRef) {
- this[_elementType] = new Model.Reference(this.source, _idRef);
-}
+BaseElement.prototype.setReference = function(_elementType, _idRef) {
+ this[_elementType] = new Reference(this.source, _idRef);
+};
-Model.Element.prototype.getReference = function(_elementType) {
+BaseElement.prototype.getReference = function(_elementType) {
if (typeof this[_elementType] !== "undefined") {
return this[_elementType].getContents();
}
-}
+};
-Model.Element.prototype.getRelated = function(_elementType, _global) {
+BaseElement.prototype.getRelated = function(_elementType, _global) {
_global = (typeof _global !== "undefined" && _global);
var _this = this;
return this.source.getList(_elementType, _global).filter(function(_el) {
var _ref = _el[_this.elementType];
return _ref && _ref.isOrHasId(_this.id);
});
-}
+};
-Model.Element.prototype.on = function(_event, _callback) {
+BaseElement.prototype.on = function(_event, _callback) {
if (typeof this.__events[_event] === "undefined") {
this.__events[_event] = [];
}
this.__events[_event].push(_callback);
-}
+};
-Model.Element.prototype.off = function(_event, _callback) {
+BaseElement.prototype.off = function(_event, _callback) {
if (typeof this.__events[_event] !== "undefined") {
this.__events[_event] = ns._(this.__events[_event]).reject(function(_fn) {
return _fn === _callback;
});
}
-}
+};
-Model.Element.prototype.trigger = function(_event, _data) {
+BaseElement.prototype.trigger = function(_event, _data) {
var _element = this;
ns._(this.__events[_event]).each(function(_callback) {
_callback.call(_element, _data);
});
-}
+};
/* */
-Model.Playable = function(_id, _source) {
- Model.Element.call(this, _id, _source);
+var Playable = Model.Playable = function(_id, _source) {
+ BaseElement.call(this, _id, _source);
if (typeof _source === "undefined") {
return;
}
this.elementType = 'playable';
- this.currentTime = new Model.Time();
+ this.currentTime = new Time();
this.volume = .5;
this.paused = true;
this.muted = false;
@@ -914,188 +750,188 @@
});
this.on("loadedmetadata", function() {
_this.loadedMetadata = true;
- })
-}
+ });
+};
-Model.Playable.prototype = new Model.Element();
+extendPrototype(Playable, BaseElement);
-Model.Playable.prototype.getCurrentTime = function() {
+Playable.prototype.getCurrentTime = function() {
return this.currentTime;
-}
+};
-Model.Playable.prototype.getVolume = function() {
+Playable.prototype.getVolume = function() {
return this.volume;
-}
+};
-Model.Playable.prototype.getPaused = function() {
+Playable.prototype.getPaused = function() {
return this.paused;
-}
+};
-Model.Playable.prototype.getMuted = function() {
+Playable.prototype.getMuted = function() {
return this.muted;
-}
+};
-Model.Playable.prototype.setCurrentTime = function(_time) {
+Playable.prototype.setCurrentTime = function(_time) {
this.trigger("setcurrenttime",_time);
-}
+};
-Model.Playable.prototype.setVolume = function(_vol) {
+Playable.prototype.setVolume = function(_vol) {
this.trigger("setvolume",_vol);
-}
+};
-Model.Playable.prototype.setMuted = function(_muted) {
+Playable.prototype.setMuted = function(_muted) {
this.trigger("setmuted",_muted);
-}
+};
-Model.Playable.prototype.play = function() {
+Playable.prototype.play = function() {
this.trigger("setplay");
-}
+};
-Model.Playable.prototype.pause = function() {
+Playable.prototype.pause = function() {
this.trigger("setpause");
-}
+};
-Model.Playable.prototype.show = function() {}
+Playable.prototype.show = function() {};
-Model.Playable.prototype.hide = function() {}
+Playable.prototype.hide = function() {};
/* */
-Model.Media = function(_id, _source) {
- Model.Playable.call(this, _id, _source);
+var Media = Model.Media = function(_id, _source) {
+ Playable.call(this, _id, _source);
this.elementType = 'media';
- this.duration = new Model.Time();
+ this.duration = new Time();
this.video = '';
var _this = this;
-}
+};
-Model.Media.prototype = new Model.Playable();
+extendPrototype(Media, Playable);
/* Default functions to be overriden by players */
-Model.Media.prototype.setDuration = function(_durationMs) {
+Media.prototype.setDuration = function(_durationMs) {
this.duration.setMilliseconds(_durationMs);
-}
+};
-Model.Media.prototype.getAnnotations = function() {
+Media.prototype.getAnnotations = function() {
return this.getRelated("annotation");
-}
+};
-Model.Media.prototype.getAnnotationsByTypeTitle = function(_title) {
+Media.prototype.getAnnotationsByTypeTitle = function(_title) {
var _annTypes = this.source.getAnnotationTypes().searchByTitle(_title).pluck("id");
if (_annTypes.length) {
return this.getAnnotations().filter(function(_annotation) {
return ns._(_annTypes).indexOf(_annotation.getAnnotationType().id) !== -1;
});
} else {
- return new Model.List(this.source.directory)
+ return new List(this.source.directory)
}
-}
+};
/* */
-Model.Tag = function(_id, _source) {
- Model.Element.call(this, _id, _source);
+var Tag = Model.Tag = function(_id, _source) {
+ BaseElement.call(this, _id, _source);
this.elementType = 'tag';
-}
+};
-Model.Tag.prototype = new Model.Element();
+extendPrototype(Tag, BaseElement);
-Model.Tag.prototype.getAnnotations = function() {
+Tag.prototype.getAnnotations = function() {
return this.getRelated("annotation");
-}
+};
/* */
-Model.AnnotationType = function(_id, _source) {
- Model.Element.call(this, _id, _source);
+var AnnotationType = Model.AnnotationType = function(_id, _source) {
+ BaseElement.call(this, _id, _source);
this.elementType = 'annotationType';
-}
+};
-Model.AnnotationType.prototype = new Model.Element();
+extendPrototype(AnnotationType, BaseElement);
-Model.AnnotationType.prototype.getAnnotations = function() {
+AnnotationType.prototype.getAnnotations = function() {
return this.getRelated("annotation");
-}
+};
/* Annotation
* */
-Model.Annotation = function(_id, _source) {
- Model.Element.call(this, _id, _source);
+var Annotation = Model.Annotation = function(_id, _source) {
+ BaseElement.call(this, _id, _source);
this.elementType = 'annotation';
- this.begin = new Model.Time();
- this.end = new Model.Time();
- this.tag = new Model.Reference(_source, []);
+ this.begin = new Time();
+ this.end = new Time();
+ this.tag = new Reference(_source, []);
this.playing = false;
var _this = this;
this.on("click", function() {
_this.getMedia().setCurrentTime(_this.begin);
});
-}
+};
-Model.Annotation.prototype = new Model.Element();
+extendPrototype(Annotation, BaseElement);
-Model.Annotation.prototype.setBegin = function(_beginMs) {
+Annotation.prototype.setBegin = function(_beginMs) {
this.begin.setMilliseconds(Math.max(0,_beginMs));
this.trigger("change-begin");
if (this.end < this.begin) {
this.setEnd(this.begin);
}
-}
+};
-Model.Annotation.prototype.setEnd = function(_endMs) {
+Annotation.prototype.setEnd = function(_endMs) {
this.end.setMilliseconds(Math.min(_endMs, this.getMedia().duration.milliseconds));
this.trigger("change-end");
if (this.end < this.begin) {
this.setBegin(this.end);
}
-}
+};
-Model.Annotation.prototype.setDuration = function(_durMs) {
+Annotation.prototype.setDuration = function(_durMs) {
this.setEnd(_durMs + this.begin.milliseconds);
-}
+};
-Model.Annotation.prototype.setMedia = function(_idRef) {
+Annotation.prototype.setMedia = function(_idRef) {
this.setReference("media", _idRef);
-}
+};
-Model.Annotation.prototype.getMedia = function() {
+Annotation.prototype.getMedia = function() {
return this.getReference("media");
-}
+};
-Model.Annotation.prototype.setAnnotationType = function(_idRef) {
+Annotation.prototype.setAnnotationType = function(_idRef) {
this.setReference("annotationType", _idRef);
-}
+};
-Model.Annotation.prototype.getAnnotationType = function() {
+Annotation.prototype.getAnnotationType = function() {
return this.getReference("annotationType");
-}
+};
-Model.Annotation.prototype.setTags = function(_idRefs) {
+Annotation.prototype.setTags = function(_idRefs) {
this.setReference("tag", _idRefs);
-}
+};
-Model.Annotation.prototype.getTags = function() {
+Annotation.prototype.getTags = function() {
return this.getReference("tag");
-}
+};
-Model.Annotation.prototype.getTagTexts = function() {
+Annotation.prototype.getTagTexts = function() {
return this.getTags().getTitles();
-}
+};
-Model.Annotation.prototype.getDuration = function() {
- return new Model.Time(this.end.milliseconds - this.begin.milliseconds)
-}
+Annotation.prototype.getDuration = function() {
+ return new Time(this.end.milliseconds - this.begin.milliseconds)
+};
/* */
-Model.MashedAnnotation = function(_mashup, _annotation) {
- Model.Element.call(this, _mashup.id + "_" + _annotation.id, _annotation.source);
+var MashedAnnotation = Model.MashedAnnotation = function(_mashup, _annotation) {
+ BaseElement.call(this, _mashup.id + "_" + _annotation.id, _annotation.source);
this.elementType = 'mashedAnnotation';
this.annotation = _annotation;
- this.begin = new Model.Time();
- this.end = new Model.Time();
- this.duration = new Model.Time();
+ this.begin = new Time();
+ this.end = new Time();
+ this.duration = new Time();
this.title = this.annotation.title;
this.description = this.annotation.description;
this.color = this.annotation.color;
@@ -1109,43 +945,43 @@
this.on("leave", function() {
_this.annotation.trigger("leave");
});
-}
+};
-Model.MashedAnnotation.prototype = new Model.Element(null);
+extendPrototype(MashedAnnotation, BaseElement);
-Model.MashedAnnotation.prototype.getMedia = function() {
+MashedAnnotation.prototype.getMedia = function() {
return this.annotation.getReference("media");
-}
+};
-Model.MashedAnnotation.prototype.getAnnotationType = function() {
+MashedAnnotation.prototype.getAnnotationType = function() {
return this.annotation.getReference("annotationType");
-}
+};
-Model.MashedAnnotation.prototype.getTags = function() {
+MashedAnnotation.prototype.getTags = function() {
return this.annotation.getReference("tag");
-}
+};
-Model.MashedAnnotation.prototype.getTagTexts = function() {
+MashedAnnotation.prototype.getTagTexts = function() {
return this.annotation.getTags().getTitles();
-}
+};
-Model.MashedAnnotation.prototype.getDuration = function() {
+MashedAnnotation.prototype.getDuration = function() {
return this.annotation.getDuration();
-}
+};
-Model.MashedAnnotation.prototype.setBegin = function(_begin) {
+MashedAnnotation.prototype.setBegin = function(_begin) {
this.begin.setMilliseconds(_begin);
this.duration.setMilliseconds(this.annotation.getDuration());
this.end.setMilliseconds(_begin + this.duration);
-}
+};
/* */
-Model.Mashup = function(_id, _source) {
- Model.Playable.call(this, _id, _source);
+var Mashup = Model.Mashup = function(_id, _source) {
+ Playable.call(this, _id, _source);
this.elementType = 'mashup';
- this.duration = new Model.Time();
- this.segments = new Model.List(_source.directory);
+ this.duration = new Time();
+ this.segments = new List(_source.directory);
this.loaded = false;
var _this = this;
this._updateTimes = function() {
@@ -1154,21 +990,21 @@
}
this.on("add", this._updateTimes);
this.on("remove", this._updateTimes);
-}
+};
-Model.Mashup.prototype = new Model.Playable();
+extendPrototype(Mashup, Playable);
-Model.Mashup.prototype.updateTimes = function() {
+Mashup.prototype.updateTimes = function() {
var _time = 0;
this.segments.forEach(function(_segment) {
_segment.setBegin(_time);
_time = _segment.end;
});
this.duration.setMilliseconds(_time);
-}
+};
-Model.Mashup.prototype.addAnnotation = function(_annotation, _defer) {
- var _mashedAnnotation = new Model.MashedAnnotation(this, _annotation),
+Mashup.prototype.addAnnotation = function(_annotation, _defer) {
+ var _mashedAnnotation = new MashedAnnotation(this, _annotation),
_defer = _defer || false;
this.segments.push(_mashedAnnotation);
_annotation.on("change-begin", this._updateTimes);
@@ -1176,33 +1012,33 @@
if (!_defer) {
this.trigger("add");
}
-}
+};
-Model.Mashup.prototype.addAnnotationById = function(_elId, _defer) {
+Mashup.prototype.addAnnotationById = function(_elId, _defer) {
var _annotation = this.source.getElement(_elId),
_defer = _defer || false;
if (typeof _annotation !== "undefined") {
this.addAnnotation(_annotation, _defer);
}
-}
+};
-Model.Mashup.prototype.addAnnotations = function(_segments) {
+Mashup.prototype.addAnnotations = function(_segments) {
var _this = this;
ns._(_segments).forEach(function(_segment) {
_this.addAnnotation(_segment, true);
});
this.trigger("add");
-}
+};
-Model.Mashup.prototype.addAnnotationsById = function(_segments) {
+Mashup.prototype.addAnnotationsById = function(_segments) {
var _this = this;
ns._(_segments).forEach(function(_segment) {
_this.addAnnotationById(_segment, true);
});
this.trigger("add");
-}
+};
-Model.Mashup.prototype.removeAnnotation = function(_annotation, _defer) {
+Mashup.prototype.removeAnnotation = function(_annotation, _defer) {
var _defer = _defer || false;
_annotation.off("change-begin", this._updateTimes);
_annotation.off("change-end", this._updateTimes);
@@ -1210,9 +1046,9 @@
if (!_defer) {
this.trigger("remove");
}
-}
+};
-Model.Mashup.prototype.removeAnnotationById = function(_annId, _defer) {
+Mashup.prototype.removeAnnotationById = function(_annId, _defer) {
var _defer = _defer || false;
var _annotation = this.source.getElement(_annId);
@@ -1222,72 +1058,72 @@
if (!_defer) {
this.trigger("remove");
}
-}
+};
-Model.Mashup.prototype.setAnnotations = function(_segments) {
+Mashup.prototype.setAnnotations = function(_segments) {
while (this.segments.length) {
this.removeAnnotation(this.segments[0].annotation, true);
}
this.addAnnotations(_segments);
-}
+};
-Model.Mashup.prototype.setAnnotationsById = function(_segments) {
+Mashup.prototype.setAnnotationsById = function(_segments) {
while (this.segments.length) {
this.removeAnnotation(this.segments[0].annotation, true);
}
this.addAnnotationsById(_segments);
-}
+};
-Model.Mashup.prototype.hasAnnotation = function(_annotation) {
+Mashup.prototype.hasAnnotation = function(_annotation) {
return !!ns._(this.segments).find(function(_s) {
return _s.annotation === _annotation
});
-}
+};
-Model.Mashup.prototype.getAnnotation = function(_annotation) {
+Mashup.prototype.getAnnotation = function(_annotation) {
return ns._(this.segments).find(function(_s) {
return _s.annotation === _annotation
});
-}
+};
-Model.Mashup.prototype.getAnnotationById = function(_id) {
+Mashup.prototype.getAnnotationById = function(_id) {
return ns._(this.segments).find(function(_s) {
return _s.annotation.id === _id
});
-}
+};
-Model.Mashup.prototype.getAnnotations = function() {
+Mashup.prototype.getAnnotations = function() {
return this.segments;
-}
+};
-Model.Mashup.prototype.getOriginalAnnotations = function() {
- var annotations = new Model.List(this.source.directory);
+Mashup.prototype.getOriginalAnnotations = function() {
+ var annotations = new List(this.source.directory);
this.segments.forEach(function(_s) {
annotations.push(_s.annotation);
});
return annotations;
-}
+};
-Model.Mashup.prototype.getMedias = function() {
- var medias = new Model.List(this.source.directory);
+Mashup.prototype.getMedias = function() {
+ var medias = new List(this.source.directory);
this.segments.forEach(function(_annotation) {
medias.push(_annotation.getMedia())
})
return medias;
-}
+};
-Model.Mashup.prototype.getAnnotationsByTypeTitle = function(_title) {
+Mashup.prototype.getAnnotationsByTypeTitle = function(_title) {
var _annTypes = this.source.getAnnotationTypes().searchByTitle(_title).pluck("id");
if (_annTypes.length) {
return this.getAnnotations().filter(function(_annotation) {
return ns._(_annTypes).indexOf(_annotation.getAnnotationType().id) !== -1;
});
} else {
- return new Model.List(this.source.directory)
+ return new List(this.source.directory)
}
-}
+};
-Model.Mashup.prototype.getAnnotationAtTime = function(_time) {
+Mashup.prototype.getAnnotationAtTime = function(_time) {
var _list = this.segments.filter(function(_annotation) {
return _annotation.begin <= _time && _annotation.end > _time;
});
@@ -1296,22 +1132,22 @@
} else {
return undefined;
}
-}
+};
-Model.Mashup.prototype.getMediaAtTime = function(_time) {
+Mashup.prototype.getMediaAtTime = function(_time) {
var _annotation = this.getAnnotationAtTime(_time);
if (typeof _annotation !== "undefined") {
return _annotation.getMedia();
} else {
return undefined;
}
-}
+};
/* */
-Model.Source = function(_config) {
- Model.Element.call(this, false, this);
- this.status = Model._SOURCE_STATUS_EMPTY;
+var Source = Model.Source = function(_config) {
+ BaseElement.call(this, false, this);
+ this.status = _SOURCE_STATUS_EMPTY;
this.elementType = "source";
if (typeof _config !== "undefined") {
var _this = this;
@@ -1322,117 +1158,117 @@
this.contents = {};
this.get();
}
-}
+};
-Model.Source.prototype = new Model.Element();
+extendPrototype(Source, BaseElement);
-Model.Source.prototype.addList = function(_listId, _contents) {
+Source.prototype.addList = function(_listId, _contents) {
if (typeof this.contents[_listId] === "undefined") {
- this.contents[_listId] = new Model.List(this.directory);
+ this.contents[_listId] = new List(this.directory);
}
this.contents[_listId].addElements(_contents);
-}
+};
-Model.Source.prototype.getList = function(_listId, _global) {
+Source.prototype.getList = function(_listId, _global) {
_global = (typeof _global !== "undefined" && _global);
if (_global) {
return this.directory.getGlobalList().filter(function(_e) {
return (_e.elementType === _listId);
});
} else {
- return this.contents[_listId] || new IriSP.Model.List(this.directory);
+ return this.contents[_listId] || new IriSP.List(this.directory);
}
-}
+};
-Model.Source.prototype.forEach = function(_callback) {
+Source.prototype.forEach = function(_callback) {
var _this = this;
ns._(this.contents).forEach(function(_value, _key) {
_callback.call(_this, _value, _key);
})
-}
+};
-Model.Source.prototype.getElement = function(_elId) {
+Source.prototype.getElement = function(_elId) {
return this.directory.getElement(_elId);
-}
+};
-Model.Source.prototype.get = function() {
- this.status = Model._SOURCE_STATUS_WAITING;
+Source.prototype.get = function() {
+ this.status = _SOURCE_STATUS_WAITING;
this.handleCallbacks();
-}
+};
/* We defer the callbacks calls so they execute after the queue is cleared */
-Model.Source.prototype.deferCallback = function(_callback) {
+Source.prototype.deferCallback = function(_callback) {
var _this = this;
ns._.defer(function() {
_callback.call(_this);
});
-}
+};
-Model.Source.prototype.handleCallbacks = function() {
- this.status = Model._SOURCE_STATUS_READY;
+Source.prototype.handleCallbacks = function() {
+ this.status = _SOURCE_STATUS_READY;
while (this.callbackQueue.length) {
this.deferCallback(this.callbackQueue.splice(0,1)[0]);
}
-}
-Model.Source.prototype.onLoad = function(_callback) {
- if (this.status === Model._SOURCE_STATUS_READY) {
+};
+Source.prototype.onLoad = function(_callback) {
+ if (this.status === _SOURCE_STATUS_READY) {
this.deferCallback(_callback);
} else {
this.callbackQueue.push(_callback);
}
-}
+};
-Model.Source.prototype.serialize = function() {
+Source.prototype.serialize = function() {
return this.serializer.serialize(this);
-}
+};
-Model.Source.prototype.deSerialize = function(_data) {
+Source.prototype.deSerialize = function(_data) {
this.serializer.deSerialize(_data, this);
-}
+};
-Model.Source.prototype.getAnnotations = function(_global) {
+Source.prototype.getAnnotations = function(_global) {
_global = (typeof _global !== "undefined" && _global);
return this.getList("annotation", _global);
-}
+};
-Model.Source.prototype.getMedias = function(_global) {
+Source.prototype.getMedias = function(_global) {
_global = (typeof _global !== "undefined" && _global);
return this.getList("media", _global);
-}
+};
-Model.Source.prototype.getTags = function(_global) {
+Source.prototype.getTags = function(_global) {
_global = (typeof _global !== "undefined" && _global);
return this.getList("tag", _global);
-}
+};
-Model.Source.prototype.getMashups = function(_global) {
+Source.prototype.getMashups = function(_global) {
_global = (typeof _global !== "undefined" && _global);
return this.getList("mashup", _global);
-}
+};
-Model.Source.prototype.getAnnotationTypes = function(_global) {
+Source.prototype.getAnnotationTypes = function(_global) {
_global = (typeof _global !== "undefined" && _global);
return this.getList("annotationType", _global);
-}
+};
-Model.Source.prototype.getAnnotationsByTypeTitle = function(_title, _global) {
+Source.prototype.getAnnotationsByTypeTitle = function(_title, _global) {
_global = (typeof _global !== "undefined" && _global);
- var _res = new Model.List(this.directory),
+ var _res = new List(this.directory),
_annTypes = this.getAnnotationTypes(_global).searchByTitle(_title);
_annTypes.forEach(function(_annType) {
_res.addElements(_annType.getAnnotations(_global));
})
return _res;
-}
+};
-Model.Source.prototype.getDuration = function() {
+Source.prototype.getDuration = function() {
var _m = this.currentMedia;
if (typeof _m !== "undefined") {
return this.currentMedia.duration;
}
-}
+};
-Model.Source.prototype.getCurrentMedia = function(_opts) {
+Source.prototype.getCurrentMedia = function(_opts) {
if (typeof this.currentMedia === "undefined") {
if (_opts.is_mashup) {
var _mashups = this.getMashups();
@@ -1447,28 +1283,28 @@
}
}
return this.currentMedia;
-}
+};
-Model.Source.prototype.merge = function(_source) {
+Source.prototype.merge = function(_source) {
var _this = this;
_source.forEach(function(_value, _key) {
_this.getList(_key).addElements(_value);
});
-}
+};
/* */
-Model.RemoteSource = function(_config) {
- Model.Source.call(this, _config);
-}
+var RemoteSource = Model.RemoteSource = function(_config) {
+ Source.call(this, _config);
+};
-Model.RemoteSource.prototype = new Model.Source();
+extendPrototype(RemoteSource, Source);
-Model.RemoteSource.prototype.get = function() {
- this.status = Model._SOURCE_STATUS_WAITING;
+RemoteSource.prototype.get = function() {
+ this.status = _SOURCE_STATUS_WAITING;
var _this = this,
urlparams = this.url_params || {},
- dataType = (Model.isLocalURL(this.url) ? "json" : "jsonp");
+ dataType = (isLocalURL(this.url) ? "json" : "jsonp");
urlparams.format = dataType;
ns.jQuery.ajax({
url: this.url,
@@ -1480,361 +1316,178 @@
_this.handleCallbacks();
}
});
-}
+};
/* */
-Model.Directory = function() {
+var Directory = Model.Directory = function() {
this.remoteSources = {};
this.elements = {};
-}
+};
-Model.Directory.prototype.remoteSource = function(_properties) {
+Directory.prototype.remoteSource = function(_properties) {
if (typeof _properties !== "object" || typeof _properties.url === "undefined") {
- throw "Error : Model.Directory.remoteSource(configuration): configuration.url is undefined";
+ throw "Error : Directory.remoteSource(configuration): configuration.url is undefined";
}
var _config = ns._({ directory: this }).extend(_properties);
_config.url_params = _config.url_params || {};
var _hash = _config.url + "?" + ns.jQuery.param(_config.url_params);
if (typeof this.remoteSources[_hash] === "undefined") {
- this.remoteSources[_hash] = new Model.RemoteSource(_config);
+ this.remoteSources[_hash] = new RemoteSource(_config);
}
return this.remoteSources[_hash];
-}
+};
-Model.Directory.prototype.newLocalSource = function(_properties) {
+Directory.prototype.newLocalSource = function(_properties) {
var _config = ns._({ directory: this }).extend(_properties),
- _res = new Model.Source(_config);
+ _res = new Source(_config);
return _res;
-}
+};
-Model.Directory.prototype.getElement = function(_id) {
+Directory.prototype.getElement = function(_id) {
return this.elements[_id];
-}
+};
-Model.Directory.prototype.addElement = function(_element) {
+Directory.prototype.addElement = function(_element) {
this.elements[_element.id] = _element;
-}
+};
-Model.Directory.prototype.getGlobalList = function() {
- var _res = new Model.List(this);
+Directory.prototype.getGlobalList = function() {
+ var _res = new List(this);
_res.addIds(ns._(this.elements).keys());
return _res;
-}
-
+};
return Model;
})(IriSP);
-/* END model.js */
-
-IriSP.language = 'en';
+/* END js */
-IriSP.libFiles = {
- defaultDir : "js/libs/",
- inDefaultDir : {
- underscore : "underscore-min.js",
- Mustache : "mustache.js",
- jQuery : "jquery.min.js",
- jQueryUI : "jquery-ui.min.js",
- swfObject : "swfobject.js",
- cssjQueryUI : "jquery-ui.css",
- popcorn : "popcorn-complete.min.js",
- jwplayer : "jwplayer.js",
- raphael : "raphael-min.js",
- tracemanager : "tracemanager.js",
- jwPlayerSWF : "player.swf",
- json : "json2.js",
- zeroClipboardJs: "ZeroClipboard.js",
- zeroClipboardSwf: "ZeroClipboard.swf",
- backbone: "backbone.js",
- backboneRelational: "backbone-relational.js",
- paper: "paper.js",
- jqueryMousewheel: "jquery.mousewheel.min.js",
- renkanPublish: "renkan-publish.js",
- processing: "processing-1.3.6.min.js",
- recordMicSwf: "record_mic.swf"
- },
- locations : {
- // use to define locations outside default_dir
- },
- cdn : {
- jQuery : "http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.7.2.min.js",
- jQueryUI : "http://ajax.aspnetcdn.com/ajax/jquery.ui/1.8.22/jquery-ui.min.js",
- swfObject : "http://ajax.googleapis.com/ajax/libs/swfobject/2.2/swfobject.js",
- cssjQueryUI : "http://ajax.aspnetcdn.com/ajax/jquery.ui/1.8.22/themes/ui-lightness/jquery-ui.css",
- underscore : "http://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.3.3/underscore-min.js",
- Mustache : "http://cdnjs.cloudflare.com/ajax/libs/mustache.js/0.5.0-dev/mustache.min.js",
- raphael : "http://cdnjs.cloudflare.com/ajax/libs/raphael/2.1.0/raphael-min.js",
- json : "http://cdnjs.cloudflare.com/ajax/libs/json2/20110223/json2.js",
- popcorn: "http://cdn.popcornjs.org/code/dist/popcorn-complete.min.js"
- },
- useCdn : false
-}
-
-IriSP.widgetsDir = 'widgets';
+/* HTML player, to be reused in a widget, or elsewhere */
-IriSP.widgetsRequirements = {
- PopcornPlayer: {
- noCss: true,
- requires: [ "popcorn" ]
- },
- HtmlPlayer: {
- noCss: true
- },
- JwpPlayer: {
- noCss: true,
- requires: [ "jwplayer" ]
- },
- DailymotionPlayer: {
- noCss: true,
- requires: [ "swfObject" ]
- },
- AdaptivePlayer: {
- noCss: true
- },
- AutoPlayer: {
- noCss: true
- },
- AnnotationsList: {
- requires: [ "jwplayer" ]
- },
- Sparkline: {
- noCss: true,
- requires: [ "raphael" ]
- },
- Arrow: {
- noCss: true,
- requires: [ "raphael" ]
- },
- Mediafragment: {
- noCss: true
- },
- Trace : {
- noCss: true,
- requires: [ "tracemanager" ]
- },
- Slideshare: {
- requires: [ "swfObject" ]
- },
- Social: {
- requires: [ "zeroClipboardJs" ]
- },
- Renkan: {
- requires: [ "backbone", "backboneRelational", "paper", "jqueryMousewheel", "renkanPublish" ]
- },
- KnowledgeConcierge: {
- requires: [ "processing" ]
- },
- MultiSegments: {
- noCss: true
+IriSP.htmlPlayer = function(media, jqselector, options) {
+
+ var opts = options || {},
+ videoURL = opts.video || media.video;
+
+ if (typeof opts.url_transform === "function") {
+ videoURL = opts.url_transform(videoURL);
}
-}
-
-IriSP.guiDefaults = {
- width : 640,
- container : 'LdtPlayer',
- spacer_div_height : 0
-}
-/* Definition of an ancestor for the Widget classes */
-
-if (typeof IriSP.Widgets === "undefined") {
- IriSP.Widgets = {}
-}
-
-/**
- * @class IriSP.Widget is an "abstract" class. It's mostly used to define some properties common to every widget.
- *
- * Note that widget constructors are never called directly by the user. Instead, the widgets are instantiated by functions
- * defined in init.js
- *
- * @constructor
- * @param player - a reference to the player widget
- * @param config - configuration options for the widget
- */
-
-
-IriSP.Widgets.Widget = function(player, config) {
+
+ var videoEl = IriSP.jQuery('<video>');
+
+ videoEl.attr({
+ width : opts.width || undefined,
+ height : opts.height || undefined
+ });
- if( typeof player === "undefined") {
- /* Probably an abstract call of the class when
- * individual widgets set their prototype */
- return;
+ if(typeof videoURL === "string"){
+ videoEl.attr("src",videoURL);
+ } else {
+ for (var i = 0; i < videoURL.length; i++) {
+ var _srcNode = IriSP.jQuery('<source>');
+ _srcNode.attr({
+ src: videoURL[i].src,
+ type: videoURL[i].type
+ });
+ videoEl.append(_srcNode);
+ }
+ }
+
+ jqselector.html(videoEl);
+
+ if (opts.autostart || opts.autoplay) {
+ videoEl.attr("autoplay", true);
}
- this.__subwidgets = [];
+ var mediaEl = videoEl[0];
- /* Setting all the configuration options */
- var _type = config.type,
- _config = IriSP._.defaults({}, config, player.config.default_options, this.defaults),
- _this = this;
+ // Binding HTML video functions to media events
+ media.on("setcurrenttime", function(_milliseconds) {
+ try {
+ mediaEl.currentTime = (_milliseconds / 1000);
+ } catch (err) {
+
+ }
+ });
- IriSP._(_config).forEach(function(_value, _key) {
- _this[_key] = _value;
+ media.on("setvolume", function(_vol) {
+ media.volume = _vol;
+ try {
+ mediaEl.volume = _vol;
+ } catch (err) {
+
+ }
});
- this.$ = IriSP.jQuery('#' + this.container);
+ media.on("setmuted", function(_muted) {
+ media.muted = _muted;
+ try {
+ mediaEl.muted = _muted;
+ } catch (err) {
+
+ }
+ });
- if (typeof this.width === "undefined") {
- this.width = this.$.width();
- } else {
- this.$.css("width", this.width);
- }
+ media.on("setplay", function() {
+ try {
+ mediaEl.play();
+ } catch (err) {
+
+ }
+ });
- if (typeof this.height !== "undefined") {
- this.$.css("height", this.height);
+ media.on("setpause", function() {
+ try {
+ mediaEl.pause();
+ } catch (err) {
+
+ }
+ });
+
+ // Binding DOM events to media
+ function getVolume() {
+ media.muted = mediaEl.muted;
+ media.volume = mediaEl.volume;
}
- /* Setting this.player at the end in case it's been overriden
- * by a configuration option of the same name :-(
- */
- this.player = player;
-
- /* Adding classes and html attributes */
- this.$.addClass("Ldt-TraceMe Ldt-Widget").attr("widget-type", _type);
+ videoEl.on("loadedmetadata", function() {
+ getVolume();
+ media.trigger("loadedmetadata");
+ media.trigger("volumechange");
+ })
- this.l10n = (
- typeof this.messages[IriSP.language] !== "undefined"
- ? this.messages[IriSP.language]
- : (
- IriSP.language.length > 2 && typeof this.messages[IriSP.language.substr(0,2)] !== "undefined"
- ? this.messages[IriSP.language.substr(0,2)]
- : this.messages["en"]
- )
- );
+ videoEl.on("timeupdate", function() {
+ media.trigger("timeupdate", new IriSP.Model.Time(1000*mediaEl.currentTime));
+ });
- /* Loading Metadata if required */
+ videoEl.on("volumechange", function() {
+ getVolume();
+ media.trigger("volumechange");
+ })
- if (this.metadata) {
- /* Getting metadata */
- this.source = player.loadMetadata(this.metadata);
-
- /* Call draw when loaded */
- this.source.onLoad(function() {
- if (_this.media_id) {
- _this.media = this.getElement(_this.media_id);
- } else {
- var _mediaopts = {
- is_mashup: _this.is_mashup || false
- }
- _this.media = this.getCurrentMedia(_mediaopts);
- }
-
- _this.draw();
- player.trigger("widget-loaded");
- });
- } else {
- this.draw();
- }
+ videoEl.on("play", function() {
+ media.trigger("play");
+ });
+
+ videoEl.on("pause", function() {
+ media.trigger("pause");
+ });
+
+ videoEl.on("seeking", function() {
+ media.trigger("seeking");
+ });
+
+ videoEl.on("seeked", function() {
+ media.trigger("seeked");
+ });
};
-
-IriSP.Widgets.Widget.prototype.defaults = {}
-
-IriSP.Widgets.Widget.prototype.template = '';
-
-IriSP.Widgets.Widget.prototype.messages = {"en":{}};
-
-IriSP.Widgets.Widget.prototype.templateToHtml = function(_template) {
- return Mustache.to_html(_template, this);
-}
-
-IriSP.Widgets.Widget.prototype.renderTemplate = function() {
- this.$.append(this.templateToHtml(this.template));
-}
-
-IriSP.Widgets.Widget.prototype.functionWrapper = function(_name) {
- var _this = this,
- _function = this[_name];
- if (typeof _function !== "undefined") {
- return function() {
- return _function.apply(_this, Array.prototype.slice.call(arguments, 0));
- }
- } else {
- console.log("Error, Unknown function IriSP.Widgets." + this.type + "." + _name)
- }
-}
-
-IriSP.Widgets.Widget.prototype.getFunctionOrName = function(_functionOrName) {
- switch (typeof _functionOrName) {
- case "function":
- return _functionOrName;
- case "string":
- return this.functionWrapper(_functionOrName);
- default:
- return undefined;
- }
-}
-
-IriSP.Widgets.Widget.prototype.onMdpEvent = function(_eventName, _functionOrName) {
- this.player.on(_eventName, this.getFunctionOrName(_functionOrName));
-}
-
-IriSP.Widgets.Widget.prototype.onMediaEvent = function(_eventName, _functionOrName) {
- this.media.on(_eventName, this.getFunctionOrName(_functionOrName));
-}
-
-IriSP.Widgets.Widget.prototype.getWidgetAnnotations = function() {
- if (typeof this.annotation_type === "undefined") {
- return this.media.getAnnotations();
- }
- if (this.annotation_type.elementType === "annotationType") {
- return this.annotation_type.getAnnotations();
- }
- return this.media.getAnnotationsByTypeTitle(this.annotation_type);
-}
-
-IriSP.Widgets.Widget.prototype.getWidgetAnnotationsAtTime = function() {
- var _time = this.media.getCurrentTime();
- return this.getWidgetAnnotations().filter(function(_annotation) {
- return _annotation.begin <= _time && _annotation.end > _time;
- });
-}
-
-IriSP.Widgets.Widget.prototype.isLoaded = function() {
- var isloaded = !IriSP._(this.__subwidgets).any(function(w) {
- return !(w && w.isLoaded());
- });
- return isloaded;
-}
-
-IriSP.Widgets.Widget.prototype.insertSubwidget = function(_selector, _widgetoptions, _propname) {
- var _id = _selector.attr("id"),
- _this = this,
- _type = _widgetoptions.type,
- $L = $LAB,
- key = this.__subwidgets.length;
- this.__subwidgets.push(null);
- if (typeof _id == "undefined") {
- _id = IriSP._.uniqueId(this.container + '_sub_widget_' + _widgetoptions.type);
- _selector.attr("id", _id);
- }
- _widgetoptions.container = _id;
- if (typeof IriSP.widgetsRequirements[_type] !== "undefined" && typeof IriSP.widgetsRequirements[_type].requires !== "undefined" ) {
- for (var _j = 0; _j < IriSP.widgetsRequirements[_type].requires.length; _j++) {
- $L.script(IriSP.getLib(IriSP.widgetsRequirements[_type].requires[_j]));
- }
- }
- $L.wait(function() {
- _this.player.loadWidget(_widgetoptions, function(_widget) {
- if (_propname) {
- _this[_propname] = _widget;
- }
- _this.__subwidgets[key] = _widget;
- });
- });
-}
-
-/**
- * This method responsible of drawing a widget on screen.
- */
-IriSP.Widgets.Widget.prototype.draw = function() {
- /* implemented by "sub-classes" */
-};/* LDT Platform Serializer */
+/* LDT Platform Serializer */
if (typeof IriSP.serializers === "undefined") {
- IriSP.serializers = {}
+ IriSP.serializers = {};
}
IriSP.serializers.ldt = {
@@ -2114,12 +1767,12 @@
_source.currentMedia = _source.getElement(_data.meta.main_media["id-ref"]);
}
}
-}
+};
-/* Used when Putting annotations on the platform */
+/* End of LDT Platform Serializer *//* ldt_annotate serializer: Used when Putting annotations on the platform */
if (typeof IriSP.serializers === "undefined") {
- IriSP.serializers = {}
+ IriSP.serializers = {};
}
IriSP.serializers.ldt_annotate = {
@@ -2190,4 +1843,527 @@
_source.addList('annotation', new IriSP.Model.List(_source.directory));
this.deserializeAnnotation(_data, _source);
}
-}
\ No newline at end of file
+};
+
+/* End ldt_annotate serializer *//* Start of defaults.js */
+
+IriSP.language = 'en';
+
+IriSP.libFiles = {
+ defaultDir : "js/libs/",
+ inDefaultDir : {
+ underscore : "underscore-min.js",
+ Mustache : "mustache.js",
+ jQuery : "jquery.min.js",
+ jQueryUI : "jquery-ui.min.js",
+ swfObject : "swfobject.js",
+ cssjQueryUI : "jquery-ui.css",
+ popcorn : "popcorn-complete.min.js",
+ jwplayer : "jwplayer.js",
+ raphael : "raphael-min.js",
+ tracemanager : "tracemanager.js",
+ jwPlayerSWF : "player.swf",
+ json : "json2.js",
+ zeroClipboardJs: "ZeroClipboard.js",
+ zeroClipboardSwf: "ZeroClipboard.swf",
+ backbone: "backbone.js",
+ backboneRelational: "backbone-relational.js",
+ paper: "paper.js",
+ jqueryMousewheel: "jquery.mousewheel.min.js",
+ renkanPublish: "renkan.js",
+ processing: "processing-1.3.6.min.js",
+ recordMicSwf: "record_mic.swf"
+ },
+ locations : {
+ // use to define locations outside default_dir
+ },
+ cdn : {
+ jQuery : "http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.7.2.min.js",
+ jQueryUI : "http://ajax.aspnetcdn.com/ajax/jquery.ui/1.8.22/jquery-ui.min.js",
+ swfObject : "http://ajax.googleapis.com/ajax/libs/swfobject/2.2/swfobject.js",
+ cssjQueryUI : "http://ajax.aspnetcdn.com/ajax/jquery.ui/1.8.22/themes/ui-lightness/jquery-ui.css",
+ underscore : "http://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.3.3/underscore-min.js",
+ Mustache : "http://cdnjs.cloudflare.com/ajax/libs/mustache.js/0.5.0-dev/mustache.min.js",
+ raphael : "http://cdnjs.cloudflare.com/ajax/libs/raphael/2.1.0/raphael-min.js",
+ json : "http://cdnjs.cloudflare.com/ajax/libs/json2/20110223/json2.js",
+ popcorn: "http://cdn.popcornjs.org/code/dist/popcorn-complete.min.js"
+ },
+ useCdn : false
+};
+
+IriSP.widgetsDir = 'widgets';
+
+IriSP.widgetsRequirements = {
+ PopcornPlayer: {
+ noCss: true,
+ requires: [ "popcorn" ]
+ },
+ HtmlPlayer: {
+ noCss: true
+ },
+ JwpPlayer: {
+ noCss: true,
+ requires: [ "jwplayer" ]
+ },
+ DailymotionPlayer: {
+ noCss: true,
+ requires: [ "swfObject" ]
+ },
+ AdaptivePlayer: {
+ noCss: true
+ },
+ AutoPlayer: {
+ noCss: true
+ },
+ AnnotationsList: {
+ requires: [ "jwplayer" ]
+ },
+ Sparkline: {
+ noCss: true,
+ requires: [ "raphael" ]
+ },
+ Arrow: {
+ noCss: true,
+ requires: [ "raphael" ]
+ },
+ Mediafragment: {
+ noCss: true
+ },
+ Trace : {
+ noCss: true,
+ requires: [ "tracemanager" ]
+ },
+ Slideshare: {
+ requires: [ "swfObject" ]
+ },
+ Social: {
+ requires: [ "zeroClipboardJs" ]
+ },
+ Renkan: {
+ requires: [ "backbone", "backboneRelational", "paper", "jqueryMousewheel", "renkanPublish" ]
+ },
+ KnowledgeConcierge: {
+ requires: [ "processing" ]
+ },
+ MultiSegments: {
+ noCss: true
+ }
+};
+
+IriSP.guiDefaults = {
+ width : 640,
+ container : 'LdtPlayer',
+ spacer_div_height : 0
+};
+
+/* End of defaults.js */
+/* widgets-container/metadataplayer.js - initialization and configuration of the widgets
+*/
+
+/* The Metadataplayer Object, single point of entry, replaces IriSP.init_player */
+
+(function(ns) {
+
+var formerJQuery, formerUnderscore, former$;
+
+var Metadataplayer = ns.Metadataplayer = function(config) {
+ ns.log("IriSP.Metadataplayer constructor");
+ for (var key in ns.guiDefaults) {
+ if (ns.guiDefaults.hasOwnProperty(key) && !config.hasOwnProperty(key)) {
+ config[key] = ns.guiDefaults[key]
+ }
+ }
+ var _container = document.getElementById(config.container);
+ _container.innerHTML = '<h3 class="Ldt-Loader">Loading... Chargement...</h3>';
+ this.sourceManager = new ns.Model.Directory();
+ this.config = config;
+ this.__events = {};
+ this.loadLibs();
+};
+
+Metadataplayer.prototype.toString = function() {
+ return 'Metadataplayer in #' + this.config.container;
+};
+
+Metadataplayer.prototype.on = function(_event, _callback) {
+ if (typeof this.__events[_event] === "undefined") {
+ this.__events[_event] = [];
+ }
+ this.__events[_event].push(_callback);
+};
+
+Metadataplayer.prototype.trigger = function(_event, _data) {
+ var _element = this;
+ ns._(this.__events[_event]).each(function(_callback) {
+ _callback.call(_element, _data);
+ });
+};
+
+Metadataplayer.prototype.loadLibs = function() {
+ ns.log("IriSP.Metadataplayer.prototype.loadLibs");
+ var $L = $LAB
+ .script(ns.getLib("Mustache"));
+
+ formerJQuery = !!window.jQuery;
+ former$ = !!window.$;
+ formerUnderscore = !!window._;
+
+ if (typeof ns.jQuery === "undefined") {
+ $L.script(ns.getLib("jQuery"));
+ }
+
+ if (typeof ns._ === "undefined") {
+ $L.script(ns.getLib("underscore"));
+ }
+
+ if (typeof window.JSON == "undefined") {
+ $L.script(ns.getLib("json"));
+ }
+
+ $L.wait()
+ .script(ns.getLib("jQueryUI"));
+
+ /* widget specific requirements */
+ for(var _i = 0; _i < this.config.widgets.length; _i++) {
+ var _t = this.config.widgets[_i].type;
+ if (typeof ns.widgetsRequirements[_t] !== "undefined" && typeof ns.widgetsRequirements[_t].requires !== "undefined" ) {
+ for (var _j = 0; _j < ns.widgetsRequirements[_t].requires.length; _j++) {
+ $L.script(ns.getLib(ns.widgetsRequirements[_t].requires[_j]));
+ }
+ }
+ }
+
+ var _this = this;
+
+ $L.wait(function() {
+ _this.onLibsLoaded();
+ });
+};
+
+Metadataplayer.prototype.onLibsLoaded = function() {
+ ns.log("IriSP.Metadataplayer.prototype.onLibsLoaded");
+ if (typeof ns.jQuery === "undefined" && typeof window.jQuery !== "undefined") {
+ ns.jQuery = window.jQuery;
+ if (former$ || formerJQuery) {
+ window.jQuery.noConflict(formerJQuery);
+ }
+ }
+ if (typeof ns._ === "undefined" && typeof window._ !== "undefined") {
+ ns._ = window._;
+ if (formerUnderscore) {
+ _.noConflict();
+ }
+ }
+ ns.loadCss(ns.getLib("cssjQueryUI"));
+ ns.loadCss(this.config.css);
+
+ this.$ = ns.jQuery('#' + this.config.container);
+ this.$.css({
+ "width": this.config.width,
+ "clear": "both"
+ });
+ if (typeof this.config.height !== "undefined") {
+ this.$.css("height", this.config.height);
+ }
+
+ this.widgets = [];
+ var _this = this;
+ ns._(this.config.widgets).each(function(widgetconf, key) {
+ _this.widgets.push(null);
+ _this.loadWidget(widgetconf, function(widget) {
+ _this.widgets[key] = widget;
+ if (widget.isLoaded()) {
+ _this.trigger("widget-loaded");
+ }
+ });
+ });
+ this.$.find('.Ldt-Loader').detach();
+
+ this.widgetsLoaded = false;
+
+ this.on("widget-loaded", function() {
+ if (_this.widgetsLoaded) {
+ return;
+ }
+ var isloaded = !ns._(_this.widgets).any(function(w) {
+ return !(w && w.isLoaded())
+ });
+ if (isloaded) {
+ _this.widgetsLoaded = true;
+ _this.trigger("widgets-loaded");
+ }
+ });
+};
+
+Metadataplayer.prototype.loadMetadata = function(_metadataInfo) {
+ if (_metadataInfo.elementType === "source") {
+ return _metadataInfo;
+ }
+ if (typeof _metadataInfo.serializer === "undefined" && typeof _metadataInfo.format !== "undefined") {
+ _metadataInfo.serializer = ns.serializers[_metadataInfo.format];
+ }
+ if (typeof _metadataInfo.url !== "undefined" && typeof _metadataInfo.serializer !== "undefined") {
+ return this.sourceManager.remoteSource(_metadataInfo);
+ } else {
+ return this.sourceManager.newLocalSource(_metadataInfo);
+ }
+};
+
+Metadataplayer.prototype.loadWidget = function(_widgetConfig, _callback) {
+ /* Creating containers if needed */
+ if (typeof _widgetConfig.container === "undefined") {
+ var _divs = this.layoutDivs(_widgetConfig.type);
+ _widgetConfig.container = _divs[0];
+ }
+
+ var _this = this;
+
+ if (typeof ns.Widgets[_widgetConfig.type] !== "undefined") {
+ ns._.defer(function() {
+ _callback(new ns.Widgets[_widgetConfig.type](_this, _widgetConfig));
+ });
+ } else {
+ /* Loading Widget CSS */
+ if (typeof ns.widgetsRequirements[_widgetConfig.type] === "undefined" || typeof ns.widgetsRequirements[_widgetConfig.type].noCss === "undefined" || !ns.widgetsRequirements[_widgetConfig.type].noCss) {
+ ns.loadCss(ns.widgetsDir + '/' + _widgetConfig.type + '.css');
+ }
+ /* Loading Widget JS */
+ $LAB.script(ns.widgetsDir + '/' + _widgetConfig.type + '.js').wait(function() {
+ _callback(new ns.Widgets[_widgetConfig.type](_this, _widgetConfig));
+ });
+ }
+};
+
+/** create a subdiv with an unique id, and a spacer div as well.
+ @param widgetName the name of the widget.
+ @return an array of the form [createdivId, spacerdivId].
+*/
+Metadataplayer.prototype.layoutDivs = function(_name, _height) {
+ if (typeof(_name) === "undefined") {
+ _name = "";
+ }
+ var newDiv = ns._.uniqueId(this.config.container + "_widget_" + _name + "_"),
+ spacerDiv = ns._.uniqueId("LdtPlayer_spacer_"),
+ divHtml = ns.jQuery('<div>')
+ .attr("id",newDiv)
+ .css({
+ width: this.config.width + "px",
+ position: "relative",
+ clear: "both"
+ }),
+ spacerHtml = ns.jQuery('<div>')
+ .attr("id",spacerDiv)
+ .css({
+ width: this.config.width + "px",
+ height: this.config.spacer_div_height + "px",
+ position: "relative",
+ clear: "both"
+ });
+ if (typeof _height !== "undefined") {
+ divHtml.css("height", _height);
+ }
+
+ this.$.append(divHtml);
+ this.$.append(spacerHtml);
+
+ return [newDiv, spacerDiv];
+};
+
+})(IriSP);
+
+/* End of widgets-container/metadataplayer.js *//* widgetsDefinition of an ancestor for the Widget classes */
+
+if (typeof IriSP.Widgets === "undefined") {
+ IriSP.Widgets = {};
+}
+
+/**
+ * @class IriSP.Widget is an "abstract" class. It's mostly used to define some properties common to every widget.
+ *
+ * Note that widget constructors are never called directly by the user. Instead, the widgets are instantiated by functions
+ * defined in init.js
+ *
+ * @constructor
+ * @param player - a reference to the player widget
+ * @param config - configuration options for the widget
+ */
+
+
+IriSP.Widgets.Widget = function(player, config) {
+
+ if( typeof player === "undefined") {
+ /* Probably an abstract call of the class when
+ * individual widgets set their prototype */
+ return;
+ }
+
+ this.__subwidgets = [];
+
+ /* Setting all the configuration options */
+ var _type = config.type,
+ _config = IriSP._.defaults({}, config, player.config.default_options, this.defaults),
+ _this = this;
+
+ IriSP._(_config).forEach(function(_value, _key) {
+ _this[_key] = _value;
+ });
+
+ this.$ = IriSP.jQuery('#' + this.container);
+
+ if (typeof this.width === "undefined") {
+ this.width = this.$.width();
+ } else {
+ this.$.css("width", this.width);
+ }
+
+ if (typeof this.height !== "undefined") {
+ this.$.css("height", this.height);
+ }
+
+ /* Setting this.player at the end in case it's been overriden
+ * by a configuration option of the same name :-(
+ */
+ this.player = player;
+
+ /* Adding classes and html attributes */
+ this.$.addClass("Ldt-TraceMe Ldt-Widget").attr("widget-type", _type);
+
+ this.l10n = (
+ typeof this.messages[IriSP.language] !== "undefined"
+ ? this.messages[IriSP.language]
+ : (
+ IriSP.language.length > 2 && typeof this.messages[IriSP.language.substr(0,2)] !== "undefined"
+ ? this.messages[IriSP.language.substr(0,2)]
+ : this.messages["en"]
+ )
+ );
+
+ /* Loading Metadata if required */
+
+ if (this.metadata) {
+ /* Getting metadata */
+ this.source = player.loadMetadata(this.metadata);
+
+ /* Call draw when loaded */
+ this.source.onLoad(function() {
+ if (_this.media_id) {
+ _this.media = this.getElement(_this.media_id);
+ } else {
+ var _mediaopts = {
+ is_mashup: _this.is_mashup || false
+ }
+ _this.media = this.getCurrentMedia(_mediaopts);
+ }
+
+ _this.draw();
+ player.trigger("widget-loaded");
+ });
+ } else {
+ this.draw();
+ }
+
+
+};
+
+IriSP.Widgets.Widget.prototype.defaults = {};
+
+IriSP.Widgets.Widget.prototype.template = '';
+
+IriSP.Widgets.Widget.prototype.messages = {"en":{}};
+
+IriSP.Widgets.Widget.prototype.templateToHtml = function(_template) {
+ return Mustache.to_html(_template, this);
+};
+
+IriSP.Widgets.Widget.prototype.renderTemplate = function() {
+ this.$.append(this.templateToHtml(this.template));
+};
+
+IriSP.Widgets.Widget.prototype.functionWrapper = function(_name) {
+ var _this = this,
+ _function = this[_name];
+ if (typeof _function !== "undefined") {
+ return function() {
+ return _function.apply(_this, Array.prototype.slice.call(arguments, 0));
+ }
+ } else {
+ console.log("Error, Unknown function IriSP.Widgets." + this.type + "." + _name)
+ }
+};
+
+IriSP.Widgets.Widget.prototype.getFunctionOrName = function(_functionOrName) {
+ switch (typeof _functionOrName) {
+ case "function":
+ return _functionOrName;
+ case "string":
+ return this.functionWrapper(_functionOrName);
+ default:
+ return undefined;
+ }
+};
+
+IriSP.Widgets.Widget.prototype.onMdpEvent = function(_eventName, _functionOrName) {
+ this.player.on(_eventName, this.getFunctionOrName(_functionOrName));
+};
+
+IriSP.Widgets.Widget.prototype.onMediaEvent = function(_eventName, _functionOrName) {
+ this.media.on(_eventName, this.getFunctionOrName(_functionOrName));
+};
+
+IriSP.Widgets.Widget.prototype.getWidgetAnnotations = function() {
+ if (typeof this.annotation_type === "undefined") {
+ return this.media.getAnnotations();
+ }
+ if (this.annotation_type.elementType === "annotationType") {
+ return this.annotation_type.getAnnotations();
+ }
+ return this.media.getAnnotationsByTypeTitle(this.annotation_type);
+};
+
+IriSP.Widgets.Widget.prototype.getWidgetAnnotationsAtTime = function() {
+ var _time = this.media.getCurrentTime();
+ return this.getWidgetAnnotations().filter(function(_annotation) {
+ return _annotation.begin <= _time && _annotation.end > _time;
+ });
+};
+
+IriSP.Widgets.Widget.prototype.isLoaded = function() {
+ var isloaded = !IriSP._(this.__subwidgets).any(function(w) {
+ return !(w && w.isLoaded());
+ });
+ return isloaded;
+};
+
+IriSP.Widgets.Widget.prototype.insertSubwidget = function(_selector, _widgetoptions, _propname) {
+ var _id = _selector.attr("id"),
+ _this = this,
+ _type = _widgetoptions.type,
+ $L = $LAB,
+ key = this.__subwidgets.length;
+ this.__subwidgets.push(null);
+ if (typeof _id == "undefined") {
+ _id = IriSP._.uniqueId(this.container + '_sub_widget_' + _widgetoptions.type);
+ _selector.attr("id", _id);
+ }
+ _widgetoptions.container = _id;
+ if (typeof IriSP.widgetsRequirements[_type] !== "undefined" && typeof IriSP.widgetsRequirements[_type].requires !== "undefined" ) {
+ for (var _j = 0; _j < IriSP.widgetsRequirements[_type].requires.length; _j++) {
+ $L.script(IriSP.getLib(IriSP.widgetsRequirements[_type].requires[_j]));
+ }
+ }
+ $L.wait(function() {
+ _this.player.loadWidget(_widgetoptions, function(_widget) {
+ if (_propname) {
+ _this[_propname] = _widget;
+ }
+ _this.__subwidgets[key] = _widget;
+ });
+ });
+};
+
+/**
+ * This method responsible of drawing a widget on screen.
+ */
+IriSP.Widgets.Widget.prototype.draw = function() {
+ /* implemented by "sub-classes" */
+};
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/web/res/metadataplayer/Renkan.css Fri Jun 07 11:55:46 2013 +0200
@@ -0,0 +1,130 @@
+
+.Ldt-Renkan-Container {
+ position: relative; width: 100%; height: 100%;
+}
+
+.Ldt-Renkan a {
+ color: #303080;
+}
+
+
+.Rk-Main {
+ position: absolute; left: 0; top: 0; right: 0; bottom: 0;
+}
+
+.Rk-Render {
+ position: absolute; left: 0; top: 0; right: 0; bottom: 0;
+ background: #ffffff;
+}
+
+/* Canvas */
+
+.Rk-Editing-Space {
+ position: absolute; left: 0; top: 35px; right: 0; bottom: 0; overflow: hidden;
+ background: -moz-radial-gradient( center, circle, #ffffff 40%, #e0e0e0 90%);
+ background: -webkit-radial-gradient( center, circle, #ffffff 40%, #e0e0e0 90%);
+ background: -ms-radial-gradient( center, circle, #ffffff 40%, #e0e0e0 90%);
+}
+
+.Rk-Editing-Space-Full {
+ top: 0;
+}
+
+.Rk-Canvas {
+ position: absolute; left: 0; top: 0; right: 0; bottom: 0; z-index: 2;
+}
+
+/* Node Labels */
+
+.Rk-Labels {
+ position: absolute; left: 0; top: 0; z-index: 1;
+ font-family: "Segoe UI", "Helvetica Neue", Arial, Helvetica, sans-serif;
+}
+
+.Rk-Label {
+ position: absolute; width: 160px; margin-left: -80px; text-align: center; font-size: 13px; line-height: 13px;
+}
+
+.Rk-Edge-Label {
+ font-size: 11px; transform-origin: 50% 0; -moz-transform-origin: 50% 0;
+ -webkit-transform-origin: 50% 0; -ms-transform-origin: 50% 0;
+}
+
+/* Editors */
+
+.Rk-Editor {
+ position: absolute; left: 0; top: 0; z-index: 3;
+}
+
+.Rk-Notifications {
+ position: absolute; right: 15px; top: 15px; width: 200px;
+ padding: 10px; border-radius: 8px; display: none;
+ color: #ffffff; font-size: 13px; text-align: center; font-weight: bold;
+ background: rgba(20,20,20,.7);
+ background: -moz-linear-gradient(top, rgba(40,40,40,.7) 20%, rgba(0,0,0,.7) 80%);
+ background: -webkit-linear-gradient(top, rgba(40,40,40,.7) 20%, rgba(0,0,0,.7) 80%);
+ background: -ms-linear-gradient(top, rgba(40,40,40,.7) 20%, rgba(0,0,0,.7) 80%);
+ filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#202020', endColorstr='#000000',GradientType=0 );
+}
+
+.Rk-CloseX {
+ float: right; cursor: pointer;
+}
+
+.Rk-Editor h2 {
+ font-size: 16px; font-weight: bold; padding: 0;
+}
+
+.Rk-Editor p, .Rk-Editor-p {
+ margin: 5px 0; font-size: 12px; clear: both; padding: 0;
+}
+
+.Rk-Editor-Label {
+ float: left; width: 80px;
+}
+
+.Rk-UserColor {
+ display: inline-block; width: 12px; height: 12px; border: 1px solid #666666; margin: -2px 2px;
+}
+
+.Rk-Display-Title a {
+ text-decoration: none; color: #000000;
+}
+
+.Rk-Display-Title a:hover {
+ text-decoration: underline;
+}
+
+.Rk-Display-URI {
+ font-style: italic;
+}
+
+.Rk-Display-ImgPreview {
+ margin: 5px auto; display: block; max-width: 255px !important; max-height: 120px !important;
+}
+
+
+.Rk-ZoomButtons {
+ position: absolute; left: 0; top: 35px; cursor: pointer;
+}
+
+.Rk-Editing-Space-Full .Rk-ZoomButtons {
+ top: 0;
+}
+
+.Rk-ZoomIn, .Rk-ZoomOut {
+ width: 21px; height: 20px; background: url(img/zoombuttons.png); margin: 5px;
+}
+
+.Rk-ZoomIn:hover {
+ background-position: 0 -20px;
+}
+
+.Rk-ZoomOut {
+ background-position: -21px 0;
+}
+
+.Rk-ZoomOut:hover {
+ background-position: -21px -20px;
+}
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/web/res/metadataplayer/Renkan.js Fri Jun 07 11:55:46 2013 +0200
@@ -0,0 +1,132 @@
+IriSP.Widgets.Renkan = function(player, config) {
+ IriSP.Widgets.Widget.call(this, player, config);
+};
+
+IriSP.Widgets.Renkan.prototype = new IriSP.Widgets.Widget();
+
+IriSP.Widgets.Renkan.prototype.defaults = {
+ annotation_regexp: /player\/([a-zA-Z0-9_-]+)\/.*id=([a-zA-Z0-9_-]+)/,
+ tag_regexp: /search=([^&=]+)/,
+ min_duration: 5000
+}
+
+IriSP.Widgets.Renkan.prototype.messages = {
+ "fr": {
+ },
+ "en": {
+ }
+}
+
+IriSP.Widgets.Renkan.prototype.template =
+ '<div class="Ldt-Renkan-Container"><div class="Ldt-Renkan"></div></div>';
+
+IriSP.Widgets.Renkan.prototype.draw = function() {
+ this.renderTemplate();
+ var _id = IriSP.Model.getUID();
+ this.$.find(".Ldt-Renkan").attr("id", _id);
+ this.renkan = new Rkns.Renkan({
+ container: _id,
+ editor_mode: false,
+ show_bins: false,
+ show_top_bar: false,
+ force_resize: true,
+ language: IriSP.language
+ });
+ this.node_times = [];
+ var _this = this,
+ _list = this.getWidgetAnnotations();
+ this.renkan.project.on("add:nodes", function(_node) {
+ var _uri = _node.get("uri"),
+ _annmatch = _uri.match(_this.annotation_regexp);
+ if (_annmatch) {
+ var _annotations = _list.filter(function(_ann) {
+ return _ann.getMedia().id == _annmatch[1] && _ann.id == _annmatch[2];
+ });
+ _annotations.forEach(function(_ann) {
+ var _duration = _ann.getDuration(),
+ _preroll = + ( _duration < _this.min_duration ) * ( _this.min_duration / 2);
+ var _nt = {
+ uri: _uri,
+ selected: false,
+ node: _node,
+ annotation: _ann,
+ begin: _ann.begin - _preroll,
+ end: _ann.end + _preroll
+ }
+ _this.node_times.push(_nt);
+ var _annselected = false,
+ _nodeselected = false;
+ _ann.on("select", function() {
+ _annselected = true;
+ if (!_nodeselected) {
+ _node.trigger("select",true);
+ }
+ });
+ _node.on("selected", function() {
+ _nodeselected = true;
+ if (!_annselected) {
+ _ann.trigger("select",true);
+ }
+ });
+ _ann.on("unselect", function() {
+ _annselected = false;
+ if (_nodeselected) {
+ _node.trigger("unselect",true);
+ }
+ });
+ _node.on("unselected", function() {
+ _nodeselected = false;
+ _nt.selected = false;
+ if (_annselected) {
+ _ann.trigger("unselect",true);
+ }
+ });
+ _node.on("clicked", function() {
+ _ann.trigger("click");
+ });
+ });
+ }
+ var _tagmatch = _uri.match(_this.tag_regexp);
+ if (_tagmatch) {
+ _node.on("select", function() {
+ _this.source.getAnnotations().search(_tagmatch[1]);
+ })
+ _node.on("unselect", function() {
+ _this.source.getAnnotations().search("");
+ })
+ }
+ });
+ Rkns.jsonIO(this.renkan, {
+ url: this.data
+ });
+
+ this.onMediaEvent("timeupdate","onTimeupdate");
+
+ this.$.find(".Rk-Editor").on("click", "a", function() {
+ var href = this.href,
+ times = _this.node_times.filter(function(t) {
+ return t.uri == href;
+ });
+ if (times.length) {
+ IriSP._(times).each(function(t) {
+ t.annotation.trigger("click");
+ });
+ return false;
+ }
+ });
+}
+
+IriSP.Widgets.Renkan.prototype.onTimeupdate = function(_time) {
+ IriSP._(this.node_times).each(function(_nt) {
+ if (_nt.begin <= _time && _nt.end >= _time) {
+ if (!_nt.selected) {
+ _nt.selected = true;
+ _nt.node.trigger("select");
+ }
+ } else {
+ if (_nt.selected) {
+ _nt.node.trigger("unselect");
+ }
+ }
+ });
+}
--- a/web/search_tweets.php Wed Apr 10 18:38:21 2013 +0200
+++ b/web/search_tweets.php Fri Jun 07 11:55:46 2013 +0200
@@ -1,35 +1,49 @@
-<?php
-
-header("Content-type: application/json");
-
-/**
- * include some common code (like we did in the 90s)
- * People still do this? ;)
- */
-include_once 'common.php';
-
-/**
- * Check for a POSTed status message to send to Twitter
- */
-if (!empty($_GET)
-&& isset($_SESSION['TWITTER_ACCESS_TOKEN'])) {
- /**
- * Easiest way to use OAuth now that we have an Access Token is to use
- * a preconfigured instance of Zend_Http_Client which automatically
- * signs and encodes all our requests without additional work
- */
- $token = unserialize($_SESSION['TWITTER_ACCESS_TOKEN']);
- $client = $token->getHttpClient($configuration);
- $client->setUri('https://api.twitter.com/1.1/search/tweets.json');
- $client->setMethod(Zend_Http_Client::GET);
- $client->setParameterGet($_GET);
- $response = $client->request();
-
- echo $response->getBody();
-
-} else {
- /**
- * Mistaken request? Some malfeasant trying something?
- */
- exit('Invalid tweet request. Oops. Sorry.');
-}
+<?php
+
+if (isset ($_GET['callback'])) {
+ header("Content-type: text/javascript");
+} else {
+ header("Content-type: application/json");
+}
+
+
+/**
+ * include some common code (like we did in the 90s)
+ * People still do this? ;)
+ */
+include_once 'common.php';
+
+/**
+ * Check for a POSTed status message to send to Twitter
+ */
+if (!empty($_GET)
+&& isset($_SESSION['TWITTER_ACCESS_TOKEN'])) {
+ /**
+ * Easiest way to use OAuth now that we have an Access Token is to use
+ * a preconfigured instance of Zend_Http_Client which automatically
+ * signs and encodes all our requests without additional work
+ */
+
+ if (isset($_GET['endpoint'])) {
+ $endpoint = $_GET['endpoint'];
+ unset($_GET['endpoint']);
+ } else {
+ $endpoint = "search/tweets";
+ }
+
+ $token = unserialize($_SESSION['TWITTER_ACCESS_TOKEN']);
+ $client = $token->getHttpClient($configuration);
+ $client->setUri("https://api.twitter.com/1.1/$endpoint.json");
+ $client->setMethod(Zend_Http_Client::GET);
+ $client->setParameterGet($_GET);
+ $response = $client->request();
+
+ echo $response->getBody();
+
+} else {
+ /**
+ * Mistaken request? Some malfeasant trying something?
+ */
+ exit('Invalid tweet request. Oops. Sorry.');
+}
+?>
\ No newline at end of file