├── stats └── .gitkeep ├── static ├── favicon.ico ├── js │ ├── main.js │ └── vendor │ │ └── jquery.timeago.js └── css │ ├── main.css │ ├── normalize.css │ └── bootstrap.min.css ├── .gitignore ├── feature.py ├── app.py ├── naivebayesclassifier.py ├── template └── index.html ├── evaluator.py ├── maxentevaluator.py ├── README.md ├── naivebayesevaluator.py ├── maxentclassifier.py └── classifier.py /stats/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alabid/sentimentstwitter/HEAD/static/favicon.ico -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | *.pyc 3 | \#*\# 4 | hidden.py 5 | trainingandtestdata/ 6 | .DS_Store 7 | model*.dat 8 | results.txt 9 | todos.txt 10 | *evaluatorstats* 11 | nohup.out 12 | evaluation.txt 13 | pics/ -------------------------------------------------------------------------------- /feature.py: -------------------------------------------------------------------------------- 1 | class Feature: 2 | 3 | def __init__(self, name, weight=None): 4 | self.name = name 5 | self.weight = weight 6 | 7 | def evaluate(self, intext): 8 | raise Exception("You must subclass 'Feature' to evaluate anything!") 9 | 10 | class UnigramFeature(Feature): 11 | ''' 12 | Feature type which simply returns 1 if the unigram is present, 0 otherwise 13 | ''' 14 | 15 | def __init__(self, unigram, weight=None): 16 | self.unigram = unigram 17 | Feature.__init__(self, 'unigram_' + unigram, weight) 18 | 19 | def evaluate(self, intext): 20 | return 1 if self.unigram in intext else 0 21 | 22 | -------------------------------------------------------------------------------- /static/js/main.js: -------------------------------------------------------------------------------- 1 | /* 2 | * main.js -> 3 | * Contains code to draw donut chart for 4 | * sentiment analysis results. 5 | */ 6 | 7 | // sent-0 -> #FFC2C2; (negative) 8 | // sent-1 -> #53DB21; (positive) 9 | var colors = ["#FFC2C2", "#53DB21"]; 10 | /* 11 | * Draw donut chart here 12 | */ 13 | function drawVisuals() { 14 | var elem = $("#visuals"); 15 | var pos = parseInt(elem.attr("pos")); 16 | var neg = parseInt(elem.attr("neg")); 17 | 18 | var dataset = { 19 | sentiments: [neg, pos] 20 | }; 21 | 22 | var width = 560, 23 | height = 400, 24 | radius = Math.min(width, height) / 2; 25 | 26 | var pie = d3.layout.pie() 27 | .sort(null); 28 | 29 | var arc = d3.svg.arc() 30 | .innerRadius(radius - 100) 31 | .outerRadius(radius - 5); 32 | 33 | var svg = d3.select($("#visuals div")[0]).append("svg") 34 | .attr("width", width) 35 | .attr("height", height) 36 | .append("g") 37 | .attr("transform" 38 | , "translate(" + width / 2 + "," + height / 2 + ")"); 39 | 40 | var path = svg.selectAll("path") 41 | .data(pie(dataset.sentiments)) 42 | .enter().append("path") 43 | .attr("fill", function(d, i) { 44 | return colors[i]; 45 | }) 46 | .attr("d", arc); 47 | } 48 | 49 | $( document ).ready(function() { 50 | $("span.timeago").timeago(); 51 | 52 | if ($("input[name=query]").attr("value").trim().length > 0) { 53 | $("#tweets").show(); 54 | 55 | drawVisuals(); 56 | 57 | $("#visuals").show(); 58 | } 59 | 60 | if (document.URL.indexOf("classifier-type=1") != -1) { 61 | $("option[value='1']").attr("selected", "selected"); 62 | } else { 63 | $("option[value='0']").attr("selected", "selected"); 64 | } 65 | }); 66 | -------------------------------------------------------------------------------- /app.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | ''' 3 | Main file to start tornado server 4 | ''' 5 | import tornado.ioloop 6 | import tornado.web 7 | import urllib 8 | import tweepy 9 | import os 10 | from hidden import * 11 | 12 | from maxentclassifier import MaximumEntropyClassifier 13 | from naivebayesclassifier import NaiveBayesClassifier 14 | 15 | # name of training set file 16 | fname = 'trainingandtestdata/training.csv' 17 | 18 | # train classifiers here first 19 | nb = NaiveBayesClassifier(fname, grams=[1,2]) 20 | nb.setThresholds(neg=1.0, pos=20.0) 21 | nb.setWeight(0.000000000005) 22 | nb.trainClassifier() 23 | ment = MaximumEntropyClassifier(fname) 24 | ment.trainClassifier() 25 | classifiers = [nb, ment] 26 | 27 | 28 | class MainHandler(tornado.web.RequestHandler): 29 | ''' 30 | Handles request to main page 31 | ''' 32 | def get(self): 33 | query = self.get_argument("query", "").strip() 34 | cchosen = int(self.get_argument("classifier-type", 0)) 35 | 36 | auth = tweepy.OAuthHandler(CONSUMER_KEY, CONSUMER_SECRET) 37 | auth.set_access_token(ACCESS_TOKEN, ACCESS_TOKEN_SECRET) 38 | 39 | api = tweepy.API(auth) 40 | 41 | # search twitter 42 | results = api.search(q=urllib.quote(query)) if len(query) > 0 else [] 43 | 44 | tweets = [] 45 | poscount = 0 46 | negcount = 0 47 | for result in results: 48 | cresult = classifiers[cchosen].classify(result.text) 49 | 50 | if cresult == 0: negcount += 1 51 | elif cresult == 1: poscount += 1 52 | else: cresult = 2 53 | 54 | tweets.append((cresult, result)) 55 | 56 | pospercent = 0 if len(results) == 0 else "%.2f" \ 57 | % (float(poscount)*100/(poscount + negcount)) 58 | negpercent = 0 if len(results) == 0 else "%.2f" \ 59 | % (float(negcount)*100/(poscount + negcount)) 60 | 61 | self.set_header("Cache-Control","no-cache") 62 | 63 | # render results of sentiment analysis on tweets in real-time 64 | self.render("index.html", 65 | poscount = poscount, 66 | negcount = negcount, 67 | pospercent = pospercent, 68 | negpercent = negpercent, 69 | query = query, 70 | tweets = tweets) 71 | 72 | 73 | if __name__ == "__main__": 74 | dirname = os.path.dirname(__file__) 75 | settings = { 76 | "static_path" : os.path.join(dirname, "static"), 77 | "template_path" : os.path.join(dirname, "template") 78 | } 79 | application = tornado.web.Application([ 80 | (r'/', MainHandler) 81 | ], **settings) 82 | 83 | application.listen(8888) 84 | tornado.ioloop.IOLoop.instance().start() 85 | 86 | -------------------------------------------------------------------------------- /naivebayesclassifier.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Implementation of Naive Bayes Classifier for tweets 3 | ''' 4 | import sys 5 | import math 6 | from classifier import Classifier 7 | 8 | class NaiveBayesClassifier(Classifier): 9 | def __init__(self, fname, *args, **kargs): 10 | Classifier.__init__(self, fname, *args, **kargs) 11 | 12 | # sometimes a threshold value is trained during Bayesian 13 | # classification to avoid classifying too many 'documents' as 14 | # one kind or the other 15 | self.thresholds = [1.0, 1.0] 16 | 17 | def setThresholds(self, neg=1.0, pos=1.0): 18 | self.thresholds = [neg, pos] 19 | 20 | def probTweetClass(self, text, c): 21 | ''' 22 | Returns the (log) probability of a tweet, given a particular class 23 | P(tweet | class) 24 | ''' 25 | features = self.getFeatures(text) 26 | p = 0 27 | for f in features: 28 | p += math.log(self.weightedProb(f, c)) 29 | return p 30 | 31 | def probClassTweet(self, text, c): 32 | ''' 33 | Returns the (log) probability of a class, given a particular tweet 34 | P(class | tweet) = P(tweet | class) x P(class) / P(tweet) 35 | But P(tweet) is constant for all classes; so forget 36 | ''' 37 | return self.probTweetClass(text, c) + math.log(self.probC(c)) 38 | 39 | def classify(self, text): 40 | ''' 41 | Returns 0 (negative) if P(class=0 | tweet) > P(class=1 | tweet) * thresholds[0] 42 | Return 1 (positive) if P(class=1 | tweet) > P(class=0 | tweet) * thresholds[1] 43 | Else return -1 (neutral) 44 | ''' 45 | p0 = self.probClassTweet(text, 0) 46 | p1 = self.probClassTweet(text, 1) 47 | 48 | if p0 > p1 + math.log(self.thresholds[0]): 49 | return 0 50 | elif p1 > p0 + math.log(self.thresholds[1]): 51 | return 1 52 | else: 53 | return -1 54 | 55 | def __repr__(self): 56 | return "Classifier info: (weight=%s, grams=%s, thresholds=%s)" % (self.weight, self.numgrams, self.thresholds) 57 | 58 | 59 | def main(): 60 | # file to get training data from 61 | fromf = 'trainingandtestdata/training.csv' 62 | naive = NaiveBayesClassifier(fromf) 63 | naive.trainClassifier() 64 | 65 | # optionally, pass in some tweet text to classify 66 | if len(sys.argv) == 2: 67 | print 68 | text = sys.argv[1] 69 | result = naive.classify(text) 70 | if result == 0: 71 | print "'%s' predicted to be Negative" % text 72 | elif result == 1: 73 | print "'%s' predicted to be Positive" % text 74 | else: 75 | print "'%s' predicted to be Neutral" % text 76 | 77 | 78 | if __name__ == "__main__": 79 | main() 80 | -------------------------------------------------------------------------------- /template/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Carleton Sentiments! 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 20 | 21 | 22 |
Carleton Sentiments
23 |
24 | 25 | using 26 | 30 | 31 |
32 | 33 | 39 | 40 | 54 | 55 | 56 | 57 | 58 | 59 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /evaluator.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Used to evaluate the performance of the generic classifier 3 | ''' 4 | import re 5 | import csv 6 | 7 | class Evaluator: 8 | ''' 9 | trainfname => name of file containing raw training data 10 | testfname => name of file containing raw testing data 11 | force == True iff user wants to overwrite classifier data 12 | allgrams -> list of ALL n-grams to use: 13 | 1, unigrams; 2, bigrams; 3, trigrams; and so on 14 | so [[1], [2]] means evaluate on unigram model, then bigrams 15 | allweights -> list of ALL weights (used in classifier.weightedProb) to use: 16 | [0.1, 1.0] means use weight=0.1, then weight=1.0 17 | ''' 18 | def __init__(self, trainfname, devfname, testfname, *args, **kargs): 19 | self.usedev = kargs.get("usedev", False) 20 | 21 | if self.usedev: 22 | self.testdata = self.readTestData(devfname) 23 | else: 24 | self.testdata = self.readTestData(testfname) 25 | 26 | self.rawfname = trainfname 27 | 28 | self.allgrams = kargs.get("allgrams") 29 | self.allweights = kargs.get("allweights") 30 | # indicator variable to display evaluation results in STDOUT 31 | self.stdout = kargs.get("stdout", False) 32 | 33 | def evaluate(self, classifier): 34 | ''' 35 | Returns some stats about how accurate classifier is on 36 | either the training set or the dev. set 37 | ''' 38 | totalneg = 0 39 | totalpos = 0 40 | correctneg = 0 41 | correctpos = 0 42 | 43 | for test in self.testdata: 44 | # check if actual result of classifier classification matches 45 | # with expected result 46 | result = test[0] 47 | text = test[1] 48 | 49 | if result == 0: 50 | if classifier.classify(text) == 0: 51 | correctneg += 1 52 | totalneg += 1 53 | elif result == 1: 54 | if classifier.classify(text) == 1: 55 | correctpos += 1 56 | totalpos += 1 57 | 58 | correctall = correctpos + correctneg 59 | totalall = totalpos + totalneg 60 | 61 | # record accuracy, correlation 62 | accpos = float(correctpos)*100/totalpos 63 | accneg = float(correctneg)*100/totalneg 64 | accall = float(correctall)*100/totalall 65 | corrall = 100-float(abs(correctpos-correctneg))*100/totalall 66 | 67 | if self.stdout: 68 | print "="*100 69 | print classifier 70 | print "Accuracy for Positives: %.2f%%" % accpos 71 | print "Accuracy for Negatives: %.2f%%" % accneg 72 | print "Accuracy for (Positives|Negatives): %.2f%%" % accall 73 | print "Correlation for (Positives|Negatives): %.2f%%" % corrall 74 | print "="*100 75 | print 76 | 77 | return [str(classifier), accpos, accneg, accall, corrall] 78 | 79 | def readTestData(self, fname): 80 | testdata = [] 81 | with open(fname) as f: 82 | r = csv.reader(f, delimiter=',', quotechar='"') 83 | for line in r: 84 | # get 0th column -> '0' if negative (class 0), '4' if positive (class 1) 85 | # '2' if neutral (class -1) 86 | # get 5th column -> contains text of tweet 87 | if line[0] == '0': 88 | polarity = 0 89 | elif line[0] == '4': 90 | polarity = 1 91 | else: 92 | polarity = -1 93 | 94 | testdata.append([polarity, 95 | re.sub(r'[,.]', r'', 96 | line[-1].lower().strip())]) 97 | return testdata 98 | 99 | def run(self): 100 | raise Exception("You must subclass 'Evaluator' and define run") 101 | -------------------------------------------------------------------------------- /maxentevaluator.py: -------------------------------------------------------------------------------- 1 | import cPickle as pickle 2 | import os 3 | import datetime 4 | import csv 5 | 6 | from evaluator import Evaluator 7 | from maxentclassifier import MaximumEntropyClassifier 8 | 9 | class MaxEntEvaluator(Evaluator): 10 | 11 | def __init__(self, trainfile, devfile, testfile, maxent_args = {}, **kargs): 12 | Evaluator.__init__(self, trainfile, devfile, testfile, **kargs) 13 | self.maxent_args = maxent_args 14 | 15 | def run(self): 16 | ''' 17 | Trains a MaximumEntropyClassifier using and evaluates 18 | the trained model 19 | ''' 20 | ent = MaximumEntropyClassifier(self.rawfname, **self.maxent_args) 21 | print 'Initialized classifier, about to train...' 22 | ent.trainClassifier() 23 | 24 | self.evaluate(ent) 25 | 26 | def runFromPickle(self, picklefile): 27 | ''' 28 | Opens the NLTK model stored in and uses that model for evaluation 29 | ''' 30 | f = open(picklefile, "rb") 31 | # Pickle stores an NLTK model 32 | ent_model = pickle.load(f) 33 | f.close() 34 | 35 | print 'Loaded classifier from', picklefile 36 | ent = MaximumEntropyClassifier(self.rawfname, **self.maxent_args) 37 | ent.setModel(ent_model) 38 | 39 | # Return everything but the classifer string 40 | return self.evaluate(ent)[1:] 41 | 42 | 43 | def testAllPickles(self, pickledir='maxentpickles/'): 44 | ''' 45 | Tests all models stored in pickles from 46 | ''' 47 | pickle_files = os.listdir(pickledir) 48 | models = [] 49 | 50 | for pick in pickle_files: 51 | print self.runFromPickle(pickledir + pick) 52 | accpos, accneg, accall, corrall = self.runFromPickle(pickledir + pick) 53 | 54 | models.append([pick, accpos, accneg, accall, corrall]) 55 | 56 | self.flushToCSV(models) 57 | 58 | 59 | def flushToCSV(self, models, resultdir='maxentresults/'): 60 | ''' 61 | Writes a file storing results from to 62 | File is named the current time stamp 63 | ''' 64 | fname = resultdir + str(datetime.datetime.now()) + '.csv' 65 | 66 | with open(fname, "wb") as f: 67 | w = csv.writer(f, delimiter=',', quotechar='"') 68 | # write out header 69 | w.writerow(["model", 70 | "accpos", 71 | "accneg", 72 | "accall", 73 | "corrall"]) 74 | for row in models: 75 | w.writerow(row) 76 | 77 | def buildManyModels(self): 78 | ''' 79 | Uses every combination of the parameters specified below to create a 80 | MaximumEntropyClassifier, train it, and evaluate it 81 | ''' 82 | all_filesubsets = [2000, 4000, 6000] 83 | 84 | all_min_occurences = [3, 5, 7] 85 | max_iter = 4 86 | all_grams = [[1], [1,2]] 87 | 88 | for filesubset in all_filesubsets: 89 | for min_occurence in all_min_occurences: 90 | for grams in all_grams: 91 | self.maxent_args = { 92 | 'filesubset' : filesubset, 93 | 'min_occurences' : min_occurence, 94 | 'max_iter' : max_iter, 95 | 'grams' : grams 96 | } 97 | ent = MaximumEntropyClassifier(self.rawfname, **self.maxent_args) 98 | print 'About to train with', self.maxent_args 99 | ent.trainClassifier() 100 | self.evaluate(ent) 101 | 102 | 103 | 104 | def main(): 105 | trainfile = "trainingandtestdata/training.csv" 106 | devfile = "trainingandtestdata/devset.csv" 107 | testfile = "trainingandtestdata/testing.csv" 108 | 109 | maxent_args = { 110 | 'filesubset' : 2000, 111 | 'min_occurences' : 4, 112 | 'max_iter' : 4, 113 | 'grams' : [1, 2] 114 | } 115 | 116 | # controls whether or not to show output/progress 117 | # == True to use the dev. set for evaluation, otherwise use the test set 118 | maxent_evaluator = MaxEntEvaluator(trainfile, 119 | devfile, 120 | testfile, 121 | maxent_args, 122 | stdout = True, 123 | usedev = False 124 | ) 125 | 126 | # Could run: 127 | # (1) To test all pickled models: maxent_evaluator.testAllPickles() 128 | # (2) To create/read one cached one model and evaluate it: maxent_evaluator.run() 129 | # (3) To build a ton of models: maxent_evaluator.buildManyModels() 130 | # This will take a LONG time, and parameters should be tweaked within the method 131 | #maxent_evaluator.run() 132 | 133 | # Uncomment the line below to run the better, pickled version 134 | maxent_evaluator.runFromPickle('maxentpickles/maxent_4000_3_2.dat') 135 | 136 | 137 | if __name__ == '__main__': 138 | main() 139 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Sentiment Analysis on Twitter 2 | ============================= 3 | 4 | ### The Problem 5 | Given a tweet (that contains some text), estimate the sentiment 6 | (negative or positive) of the tweeter. 7 | 8 | 9 | ### Training, Development, and Test Datasets 10 | Some folks at Stanford spent more than a year doing 11 | research on sentiment analysis on twitter. 12 | They published a paper 13 | [here] (http://cs.stanford.edu/people/alecmgo/papers/TwitterDistantSupervision09.pdf) 14 | and released both their training and test sets, which we used 15 | throughout our project. 16 | 17 | The training set has 1,600,000 tweets marked positive/negative, while 18 | the test set has 498 tweets. We extracted a development set of 19 | 500 tweets from the original training set to use in adjusting 20 | various parameters for both types of classifiers. 21 | 22 | ### Methods Used 23 | * **Naive Bayes Classifier:** 24 | Using the "best" 25 | parameters on our naive bayes classifier, we achieved an accuracy 26 | of 83.01%. It turns out that our Naive Bayes classifier 27 | performs better than that of Alec Go's. The Naive Bayes 28 | classifier is implemented in 29 | **naivebayesclassifier.py** and the Naive Bayes 30 | evaluator (which measures the effectiveness of the 31 | classifier) is implemented in 32 | **naivebayesevaluator.py**. 33 | 34 | Run `python naivebayesevaluator.py -g 1 1,2` to see 35 | the accuracy on the test set. 36 | 37 | Run `python naivebayesevaluator.py -h` to see all 38 | options for the Naive Bayes Evaluator. For more details, see 39 | documentation in the evaluator file. 40 | 41 | * **Maximum Entropy Classifier:** 42 | The input to an instance of the Maximum Entropy Evaluator 43 | is made up of four parameters: 44 | 45 | * Number of tweets to train on (**filesubset**) 46 | * Minimum number of occurences a feature must have appeared 47 | to be included as a feature (**min_occurences**) 48 | * The number of iterations to run GIS (**max_iter**) 49 | * What **n**-grams to use (**grams**, a list) 50 | 51 | These parameters can be tweaked in **maxentevaluator**. Generally, 52 | we noted the intuitive trend that using more data gave better results. 53 | As a result, to get the best results, we would use a large subset 54 | of tweets and a small threshold level of feature occurences 55 | (i.e. including as many **n**-grams as possible). For practical purposes 56 | of demonstration, the parameters are set to lower values which only take 57 | a few minutes to run. 58 | 59 | In order to use a better model (which achieves 76% on the test set), 60 | we've included a pickled model 61 | which was trained using 4000 tweets, unigrams and bigrams, and 62 | a threshold of 3. When a Maximum Entropy Classifier is trained, the 63 | resulting model is pickled to **maxentpickles**. While we 64 | have not included all the pickled models, the file 65 | **maxent_4000_3_2.dat** is included and can be run via 66 | **maxentevaluator**'s **runFromPickle** method. 67 | 68 | ## Web Interface 69 | Our models were trained and evaluated 70 | on data accumulated in 2006. We thought it might be useful 71 | (and cool) 72 | to evaluate our models (Naive Bayes and Max Entropy) 73 | on more recent tweets using the 74 | Twitter real-time API. So we setup a python Tornado 75 | server with a Graphical User Interface (built using 76 | HTML + CSS + Javascript) to grab tweets, perform 77 | sentiment analysis on these tweets, and display 78 | the results in an intuitive manner. 79 | 80 | If you want to run the web server, you need 81 | tornado web (which can be easily intalled via 82 | **pip** or **easy_install**. Use `python app.py` to startup the server. The file 83 | first loads both classifiers (takes about 2 minutes to load 84 | the models). Wait 85 | till you see the messages: `Model retrieved from 'model1-2.dat'` and `Max ent model built`. By 86 | default, it runs on port 8888. To see the server in 87 | action, using your favorite browser go to 88 | `http://localhost:8888/`. Pick what model you want 89 | to evaluate the tweet on, and **Search**. 90 | 91 | 92 | MIT Open Source License 93 | ----------------------- 94 | Copyright © 2012 Daniel Alabi, Nick Jones 95 | 96 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 97 | 98 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 99 | 100 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /naivebayesevaluator.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | ''' 3 | Contains implementation of Naive Bayes Classifier based upon 4 | Generic Classifier for tweets. 5 | 6 | Options: 7 | --csvout -> put results in a CSV file 8 | --stdout -> print results to STDOUT 9 | 10 | Usage: 11 | python naivebayesevaluator.py [(--csvout | --stdout) --dev -g 12 | -w 13 | -t ] 14 | 15 | For example, 16 | python naivebayesevaluator.py --csvout --dev -g 1 1,2 -w 0.1,1.5,0.5 -t 1.0,2.0,0.5 17 | 18 | should evaluate naive bayes (on the DEV set) using: 19 | 1. unigrams and unigrams + bigrams. 20 | 2. weights 0.1, 0.6, 1.1 21 | 3. threshold values 1.0, 1.5, 2.0 22 | 23 | and store the result in the file 'stats/nbevaluatorstats.csv'. 24 | python naivebayesevaluator.py -g 1 1,2 should print out accuracy of 25 | naive bayes on the TRAINING set. 26 | ''' 27 | from evaluator import Evaluator 28 | from naivebayesclassifier import NaiveBayesClassifier 29 | import csv 30 | import datetime 31 | import sys 32 | import argparse 33 | 34 | class NaiveBayesEvaluator(Evaluator): 35 | def __init__(self, trainfile, devfile, testfile, *args, **kargs): 36 | Evaluator.__init__(self, trainfile, devfile, testfile, *args, **kargs) 37 | 38 | self.allthresholds = kargs.get("allthresholds") 39 | self.csvout = kargs.get("csvout", False) 40 | self.results = [] 41 | 42 | def flushToCSV(self): 43 | fname = "stats/nbevaluatorstats%s.csv" % \ 44 | str(datetime.datetime.now()).replace(' ', '-') 45 | with open(fname, "wb") as f: 46 | w = csv.writer(f, delimiter=',', quotechar='"') 47 | # write out header 48 | w.writerow(["Classifier Info", 49 | "Accuracy for Positives (%)", 50 | "Accuracy for Negatives (%)", 51 | "Accuracy for (Positives|Negatives) (%)", 52 | "Correlation for (Positives|Negatives) (%)"]) 53 | for row in self.results: 54 | w.writerow(row) 55 | print "Flushing results of Naive Bayes evaluation into '%s'..." % fname 56 | 57 | def run(self): 58 | if not self.usedev: 59 | for grams in self.allgrams: 60 | c = NaiveBayesClassifier(self.rawfname, 61 | grams=grams) 62 | c.trainClassifier() 63 | self.stdout = True 64 | self.evaluate(c) 65 | return 66 | 67 | for grams in self.allgrams: 68 | c = NaiveBayesClassifier(self.rawfname, 69 | grams=grams) 70 | c.trainClassifier() 71 | 72 | for w in self.allweights: 73 | c.setWeight(w) 74 | 75 | for t1 in self.allthresholds: 76 | for t2 in self.allthresholds: 77 | c.setThresholds(neg=t1, pos=t2) 78 | cinfo, accpos, accneg, accall, corrall = self.evaluate(c) 79 | self.results.append([cinfo, accpos, accneg, 80 | accall, corrall]) 81 | 82 | if self.csvout: 83 | self.flushToCSV() 84 | 85 | def processGrams(glist): 86 | return [[int(eachr) for eachr in each.split(',')] for each in glist] 87 | 88 | def floatrange(start, end, step): 89 | return [start + step*x for x in range(int((end-start)/step)+1)] 90 | 91 | def processWT(wstr): 92 | start, end, step = [float(res) for res in wstr.split(',')] 93 | return floatrange(start, end, step) 94 | 95 | def main(): 96 | trainfile = "trainingandtestdata/training.csv" 97 | devfile = "trainingandtestdata/devset.csv" 98 | testfile = "trainingandtestdata/testing.csv" 99 | 100 | parser = argparse.ArgumentParser() 101 | parser.add_argument("--csvout", dest="csvout", 102 | action="store_true", default=False) 103 | parser.add_argument("--stdout", dest="stdout", 104 | action="store_true", default=False) 105 | parser.add_argument("--dev", dest="dev", 106 | action="store_true", default=False) 107 | parser.add_argument("-g", dest="g", nargs="+", 108 | metavar="x,y,z,..", required=True) 109 | parser.add_argument("-w", dest="w", 110 | metavar="START, END, STEP", required=False) 111 | parser.add_argument("-t", dest="t", 112 | metavar="START, END, STEP", required=False) 113 | 114 | args = parser.parse_args() 115 | grams = processGrams(args.g) 116 | 117 | try: 118 | if args.g and args.w and args.t: 119 | weights = processWT(args.w) 120 | thresholds = processWT(args.t) 121 | else: 122 | weights = thresholds = [] 123 | args.dev = False 124 | 125 | nbEvaluator = NaiveBayesEvaluator(trainfile, devfile, testfile, 126 | allgrams=grams, 127 | allweights=weights, 128 | allthresholds=thresholds, 129 | csvout=args.csvout, 130 | stdout=args.stdout, 131 | usedev=args.dev) 132 | nbEvaluator.run() 133 | 134 | except: 135 | parser.print_help() 136 | 137 | if __name__ == "__main__": 138 | main() 139 | -------------------------------------------------------------------------------- /static/js/vendor/jquery.timeago.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Timeago is a jQuery plugin that makes it easy to support automatically 3 | * updating fuzzy timestamps (e.g. "4 minutes ago" or "about 1 day ago"). 4 | * 5 | * @name timeago 6 | * @version 1.3.0 7 | * @requires jQuery v1.2.3+ 8 | * @author Ryan McGeary 9 | * @license MIT License - http://www.opensource.org/licenses/mit-license.php 10 | * 11 | * For usage and examples, visit: 12 | * http://timeago.yarp.com/ 13 | * 14 | * Copyright (c) 2008-2013, Ryan McGeary (ryan -[at]- mcgeary [*dot*] org) 15 | */ 16 | 17 | (function (factory) { 18 | if (typeof define === 'function' && define.amd) { 19 | // AMD. Register as an anonymous module. 20 | define(['jquery'], factory); 21 | } else { 22 | // Browser globals 23 | factory(jQuery); 24 | } 25 | }(function ($) { 26 | $.timeago = function(timestamp) { 27 | if (timestamp instanceof Date) { 28 | return inWords(timestamp); 29 | } else if (typeof timestamp === "string") { 30 | return inWords($.timeago.parse(timestamp)); 31 | } else if (typeof timestamp === "number") { 32 | return inWords(new Date(timestamp)); 33 | } else { 34 | return inWords($.timeago.datetime(timestamp)); 35 | } 36 | }; 37 | var $t = $.timeago; 38 | 39 | $.extend($.timeago, { 40 | settings: { 41 | refreshMillis: 60000, 42 | allowFuture: false, 43 | localeTitle: false, 44 | cutoff: 0, 45 | strings: { 46 | prefixAgo: null, 47 | prefixFromNow: null, 48 | suffixAgo: "ago", 49 | suffixFromNow: "from now", 50 | seconds: "less than a minute", 51 | minute: "about a minute", 52 | minutes: "%d minutes", 53 | hour: "about an hour", 54 | hours: "about %d hours", 55 | day: "a day", 56 | days: "%d days", 57 | month: "about a month", 58 | months: "%d months", 59 | year: "about a year", 60 | years: "%d years", 61 | wordSeparator: " ", 62 | numbers: [] 63 | } 64 | }, 65 | inWords: function(distanceMillis) { 66 | var $l = this.settings.strings; 67 | var prefix = $l.prefixAgo; 68 | var suffix = $l.suffixAgo; 69 | if (this.settings.allowFuture) { 70 | if (distanceMillis < 0) { 71 | prefix = $l.prefixFromNow; 72 | suffix = $l.suffixFromNow; 73 | } 74 | } 75 | 76 | var seconds = Math.abs(distanceMillis) / 1000; 77 | var minutes = seconds / 60; 78 | var hours = minutes / 60; 79 | var days = hours / 24; 80 | var years = days / 365; 81 | 82 | function substitute(stringOrFunction, number) { 83 | var string = $.isFunction(stringOrFunction) ? stringOrFunction(number, distanceMillis) : stringOrFunction; 84 | var value = ($l.numbers && $l.numbers[number]) || number; 85 | return string.replace(/%d/i, value); 86 | } 87 | 88 | var words = seconds < 45 && substitute($l.seconds, Math.round(seconds)) || 89 | seconds < 90 && substitute($l.minute, 1) || 90 | minutes < 45 && substitute($l.minutes, Math.round(minutes)) || 91 | minutes < 90 && substitute($l.hour, 1) || 92 | hours < 24 && substitute($l.hours, Math.round(hours)) || 93 | hours < 42 && substitute($l.day, 1) || 94 | days < 30 && substitute($l.days, Math.round(days)) || 95 | days < 45 && substitute($l.month, 1) || 96 | days < 365 && substitute($l.months, Math.round(days / 30)) || 97 | years < 1.5 && substitute($l.year, 1) || 98 | substitute($l.years, Math.round(years)); 99 | 100 | var separator = $l.wordSeparator || ""; 101 | if ($l.wordSeparator === undefined) { separator = " "; } 102 | return $.trim([prefix, words, suffix].join(separator)); 103 | }, 104 | parse: function(iso8601) { 105 | var s = $.trim(iso8601); 106 | s = s.replace(/\.\d+/,""); // remove milliseconds 107 | s = s.replace(/-/,"/").replace(/-/,"/"); 108 | s = s.replace(/T/," ").replace(/Z/," UTC"); 109 | s = s.replace(/([\+\-]\d\d)\:?(\d\d)/," $1$2"); // -04:00 -> -0400 110 | return new Date(s); 111 | }, 112 | datetime: function(elem) { 113 | var iso8601 = $t.isTime(elem) ? $(elem).attr("datetime") : $(elem).attr("title"); 114 | return $t.parse(iso8601); 115 | }, 116 | isTime: function(elem) { 117 | // jQuery's `is()` doesn't play well with HTML5 in IE 118 | return $(elem).get(0).tagName.toLowerCase() === "time"; // $(elem).is("time"); 119 | } 120 | }); 121 | 122 | // functions that can be called via $(el).timeago('action') 123 | // init is default when no action is given 124 | // functions are called with context of a single element 125 | var functions = { 126 | init: function(){ 127 | var refresh_el = $.proxy(refresh, this); 128 | refresh_el(); 129 | var $s = $t.settings; 130 | if ($s.refreshMillis > 0) { 131 | setInterval(refresh_el, $s.refreshMillis); 132 | } 133 | }, 134 | update: function(time){ 135 | $(this).data('timeago', { datetime: $t.parse(time) }); 136 | refresh.apply(this); 137 | }, 138 | updateFromDOM: function(){ 139 | $(this).data('timeago', { datetime: $t.parse( $t.isTime(this) ? $(this).attr("datetime") : $(this).attr("title") ) }); 140 | refresh.apply(this); 141 | } 142 | }; 143 | 144 | $.fn.timeago = function(action, options) { 145 | var fn = action ? functions[action] : functions.init; 146 | if(!fn){ 147 | throw new Error("Unknown function name '"+ action +"' for timeago"); 148 | } 149 | // each over objects here and call the requested function 150 | this.each(function(){ 151 | fn.call(this, options); 152 | }); 153 | return this; 154 | }; 155 | 156 | function refresh() { 157 | var data = prepareData(this); 158 | var $s = $t.settings; 159 | 160 | if (!isNaN(data.datetime)) { 161 | if ( $s.cutoff == 0 || distance(data.datetime) < $s.cutoff) { 162 | $(this).text(inWords(data.datetime)); 163 | } 164 | } 165 | return this; 166 | } 167 | 168 | function prepareData(element) { 169 | element = $(element); 170 | if (!element.data("timeago")) { 171 | element.data("timeago", { datetime: $t.datetime(element) }); 172 | var text = $.trim(element.text()); 173 | if ($t.settings.localeTitle) { 174 | element.attr("title", element.data('timeago').datetime.toLocaleString()); 175 | } else if (text.length > 0 && !($t.isTime(element) && element.attr("title"))) { 176 | element.attr("title", text); 177 | } 178 | } 179 | return element.data("timeago"); 180 | } 181 | 182 | function inWords(date) { 183 | return $t.inWords(distance(date)); 184 | } 185 | 186 | function distance(date) { 187 | return (new Date().getTime() - date.getTime()); 188 | } 189 | 190 | // fix for IE6 suckage 191 | document.createElement("abbr"); 192 | document.createElement("time"); 193 | })); 194 | -------------------------------------------------------------------------------- /maxentclassifier.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Implementation of Maximum Entropy Classifier for tweets 3 | ''' 4 | import sys 5 | import csv 6 | import re 7 | import os 8 | import cPickle as pickle 9 | 10 | from classifier import Classifier 11 | from evaluator import Evaluator 12 | from nltk.classify.maxent import MaxentClassifier 13 | 14 | class MaximumEntropyClassifier(Classifier): 15 | def __init__(self, rawfname, min_occurences=5, **kargs): 16 | Classifier.__init__(self, rawfname, **kargs) 17 | 18 | self.min_occurences = min_occurences 19 | 20 | # Maintains all training examples 21 | self.all_training_examples = [] 22 | 23 | # Each example contains only keys for features which occurred more 24 | # than times in the training set 25 | self.shrunk_training_examples = [] 26 | 27 | # { feature -> num times was seen } 28 | self.all_features = {} 29 | self.model = None 30 | 31 | self.filesubset = kargs.get('filesubset', 3000) 32 | 33 | self.max_iter = kargs.get('max_iter', 4) 34 | 35 | 36 | def setModel(self, model): 37 | self.model = model 38 | 39 | def initFeatures(self): 40 | ''' 41 | Grabs a sample of size (half of which are pos., half neg.) 42 | and extracts features for each example. Note that features are 1, 2, ... grams 43 | based on 44 | 45 | Since there are far too many features using all uni/bi/...grams, only features 46 | that were seen more than times (over all examples) are kept. 47 | Feature vectors are then padded with 0s for each feature that didn't appear in 48 | a given training example 49 | ''' 50 | 51 | training_sample = self.getSampleTweets(self.filesubset) 52 | print 'Training on %i lines' % len(training_sample) 53 | 54 | for each in training_sample: 55 | classification = each[0] 56 | text = each[1] 57 | feature_set = self.getFeatures(text) 58 | 59 | feature_vector = self.getFeatureDict(feature_set) 60 | self.all_training_examples.append((feature_vector, classification)) 61 | 62 | shrunk_features = self.shrinkFeatureSet() 63 | print 'Padding feature vectors. There are %i total features' % len(self.all_features) 64 | self.initShrunkExamples(shrunk_features) 65 | print 'Padding done' 66 | 67 | 68 | def shrinkFeatureSet(self): 69 | shrunk = {} 70 | 71 | # TODO make this a dict-comprehension 72 | for feat, num_ocurrences in self.all_features.iteritems(): 73 | if num_ocurrences >= self.min_occurences: 74 | shrunk[feat] = num_ocurrences 75 | 76 | print 'Shrunk down to %i features' % len(shrunk) 77 | 78 | return shrunk 79 | 80 | 81 | def initShrunkExamples(self, shrunk_features): 82 | ''' 83 | Set up , a list of tuples of the form: 84 | ({features}, classification) where {features} is made up of all features in 85 | and a 1 if the given feature was in a given example 86 | ''' 87 | 88 | # Set to include only features in 89 | # with a 1 indicating presence of a feature, 0 otherwise 90 | for i in range(len(self.all_training_examples)): 91 | example = self.all_training_examples[i] 92 | shrunk_feature_vector = {} 93 | 94 | for feat in shrunk_features: 95 | shrunk_feature_vector[feat] = 1 if feat in example[0] else 0 96 | 97 | self.shrunk_training_examples.append((shrunk_feature_vector, example[1])) 98 | 99 | 100 | def getFeatureDict(self, featureset): 101 | ''' 102 | Update to include the new features seen, and return a 103 | dictionary containing '1' for each of those features 104 | ''' 105 | feature_dict = {} 106 | 107 | for feat in featureset: 108 | feature_dict[feat] = 1 109 | self.all_features.setdefault(feat, 0) 110 | self.all_features[feat] += 1 111 | 112 | return feature_dict 113 | 114 | 115 | def trainClassifier(self): 116 | ''' 117 | Calculates features and trains the maxent classifier, storing the resulting 118 | model in 119 | ''' 120 | # check if pickled 121 | pickled_model = self.checkForPickle() 122 | if pickled_model: 123 | self.model = pickled_model 124 | else: 125 | 126 | self.initFeatures() 127 | print 'Done reading in training examples' 128 | kargs = { 129 | 'algorithm' : 'gis', 130 | } 131 | if self.max_iter != None: 132 | kargs['max_iter'] = self.max_iter 133 | 134 | self.model = MaxentClassifier.train(self.shrunk_training_examples, **kargs) 135 | self.pickleModel() 136 | print 'Max ent model built' 137 | 138 | 139 | def classify(self, text): 140 | feature_set = self.getFeatures(text) 141 | feature_vector = self.getFeatureDict(feature_set) 142 | 143 | return self.model.classify(feature_vector) 144 | 145 | def checkForPickle(self): 146 | pickle_name = self.getPickleFileName() 147 | 148 | if os.path.exists(pickle_name): 149 | f = file(pickle_name, 'rb') 150 | model = pickle.load(f) 151 | f.close() 152 | 153 | return model 154 | else: 155 | return False 156 | 157 | def pickleModel(self, model_name=None): 158 | ''' 159 | Saves the current Classifier object in a file called: 160 | "maxent___" 161 | Note that every model uses increasing number of n-grams, so the length tells us 162 | the max n-gram (i.e. length of 2 indicates we used unigrams and bigrams while length 163 | of 1 indicates we only used unigrams) 164 | ''' 165 | if model_name == None: 166 | model_name = 'maxentpickles/maxent_%i_%i_%i.dat' % \ 167 | (self.filesubset, self.min_occurences, len(self.numgrams)) 168 | 169 | outfile = open(model_name, "wb") 170 | pickle.dump(self.model, outfile) 171 | 172 | outfile.close() 173 | 174 | def getPickleFileName(self): 175 | return 'maxentpickles/maxent_%i_%i_%i.dat' % \ 176 | (self.filesubset, self.min_occurences, len(self.numgrams)) 177 | 178 | 179 | def main(): 180 | # file to get training data from 181 | trainfile = "trainingandtestdata/training.csv" 182 | testfile = "trainingandtestdata/testing.csv" 183 | 184 | maxent_args = { 185 | 'filesubset' : 3500, 186 | 'min_occurences' : 5, 187 | 'max_iter' : 4, 188 | 'grams' : [1] 189 | } 190 | ent = MaximumEntropyClassifier(trainfile, **maxent_args) 191 | ent.trainClassifier() 192 | 193 | # optionally, pass in some tweet text to classify 194 | if len(sys.argv) == 2: 195 | print ent.classify(sys.argv[1]) 196 | 197 | 198 | if __name__ == "__main__": 199 | main() 200 | -------------------------------------------------------------------------------- /static/css/main.css: -------------------------------------------------------------------------------- 1 | /*! HTML5 Boilerplate v4.3.0 | MIT License | http://h5bp.com/ */ 2 | 3 | /* 4 | * What follows is the result of much research on cross-browser styling. 5 | * Credit left inline and big thanks to Nicolas Gallagher, Jonathan Neal, 6 | * Kroc Camen, and the H5BP dev community and team. 7 | */ 8 | 9 | /* ========================================================================== 10 | Base styles: opinionated defaults 11 | ========================================================================== */ 12 | 13 | html, 14 | button, 15 | input, 16 | select, 17 | textarea { 18 | color: #222; 19 | } 20 | 21 | html { 22 | font-size: 1em; 23 | line-height: 1.4; 24 | } 25 | 26 | /* 27 | * Remove text-shadow in selection highlight: h5bp.com/i 28 | * These selection rule sets have to be separate. 29 | * Customize the background color to match your design. 30 | */ 31 | 32 | ::-moz-selection { 33 | background: #b3d4fc; 34 | text-shadow: none; 35 | } 36 | 37 | ::selection { 38 | background: #b3d4fc; 39 | text-shadow: none; 40 | } 41 | 42 | /* 43 | * A better looking default horizontal rule 44 | */ 45 | 46 | hr { 47 | display: block; 48 | height: 1px; 49 | border: 0; 50 | border-top: 1px solid #ccc; 51 | margin: 1em 0; 52 | padding: 0; 53 | } 54 | 55 | /* 56 | * Remove the gap between images, videos, audio and canvas and the bottom of 57 | * their containers: h5bp.com/i/440 58 | */ 59 | 60 | audio, 61 | canvas, 62 | img, 63 | svg, 64 | video { 65 | vertical-align: middle; 66 | } 67 | 68 | /* 69 | * Remove default fieldset styles. 70 | */ 71 | 72 | fieldset { 73 | border: 0; 74 | margin: 0; 75 | padding: 0; 76 | } 77 | 78 | /* 79 | * Allow only vertical resizing of textareas. 80 | */ 81 | 82 | textarea { 83 | resize: vertical; 84 | } 85 | 86 | /* ========================================================================== 87 | Browse Happy prompt 88 | ========================================================================== */ 89 | 90 | .browsehappy { 91 | margin: 0.2em 0; 92 | background: #ccc; 93 | color: #000; 94 | padding: 0.2em 0; 95 | } 96 | 97 | /* ========================================================================== 98 | Author's custom styles 99 | ========================================================================== */ 100 | 101 | header { 102 | color: #527bbd; 103 | margin-top: 1.2em; 104 | margin-bottom: 0.5em; 105 | line-height: 1.3em; 106 | font-family: Arial,Helvetica,sans-serif; 107 | font-size: 30pt; 108 | margin-left: 0.5em; 109 | } 110 | 111 | form { 112 | margin-left: 20px; 113 | margin-right: 10px; 114 | } 115 | 116 | span[id=using] { 117 | font-size: 20px; 118 | } 119 | 120 | input[type=text] { 121 | margin-left: 10px; 122 | margin-right: 10px; 123 | } 124 | 125 | select { 126 | margin-left: 10px; 127 | } 128 | 129 | footer { 130 | border-top-style: solid; 131 | border-top-width: 2px; 132 | border-top-color: black; 133 | padding-top: 50px; 134 | margin-left: auto; 135 | margin-right: auto; 136 | clear:both; 137 | } 138 | 139 | .sent-0 { 140 | background: #FFC2C2; 141 | border-radius: 10px; 142 | border-color: black; 143 | padding: 10px; 144 | margin: 10px; 145 | } 146 | 147 | .sent-1 { 148 | background: #53DB21; 149 | border-radius: 10px; 150 | border-color: black; 151 | padding: 20px; 152 | margin: 10px; 153 | } 154 | 155 | .sent-2 { 156 | background: #eeeeee; 157 | border-radius: 10px; 158 | border-color: black; 159 | padding: 20px; 160 | margin: 10px; 161 | } 162 | 163 | 164 | body { 165 | margin-left: 50px; 166 | margin-right: 50px; 167 | line-height: 1.4em; 168 | } 169 | 170 | ul { 171 | list-style-type: none; 172 | } 173 | 174 | footer p { 175 | text-align: center; 176 | } 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | /* ========================================================================== 193 | Helper classes 194 | ========================================================================== */ 195 | 196 | /* 197 | * Hide from both screenreaders and browsers: h5bp.com/u 198 | */ 199 | 200 | .hidden { 201 | display: none !important; 202 | visibility: hidden; 203 | } 204 | 205 | /* 206 | * Hide only visually, but have it available for screenreaders: h5bp.com/v 207 | */ 208 | 209 | .visuallyhidden { 210 | border: 0; 211 | clip: rect(0 0 0 0); 212 | height: 1px; 213 | margin: -1px; 214 | overflow: hidden; 215 | padding: 0; 216 | position: absolute; 217 | width: 1px; 218 | } 219 | 220 | /* 221 | * Extends the .visuallyhidden class to allow the element to be focusable 222 | * when navigated to via the keyboard: h5bp.com/p 223 | */ 224 | 225 | .visuallyhidden.focusable:active, 226 | .visuallyhidden.focusable:focus { 227 | clip: auto; 228 | height: auto; 229 | margin: 0; 230 | overflow: visible; 231 | position: static; 232 | width: auto; 233 | } 234 | 235 | /* 236 | * Hide visually and from screenreaders, but maintain layout 237 | */ 238 | 239 | .invisible { 240 | visibility: hidden; 241 | } 242 | 243 | /* 244 | * Clearfix: contain floats 245 | * 246 | * For modern browsers 247 | * 1. The space content is one way to avoid an Opera bug when the 248 | * `contenteditable` attribute is included anywhere else in the document. 249 | * Otherwise it causes space to appear at the top and bottom of elements 250 | * that receive the `clearfix` class. 251 | * 2. The use of `table` rather than `block` is only necessary if using 252 | * `:before` to contain the top-margins of child elements. 253 | */ 254 | 255 | .clearfix:before, 256 | .clearfix:after { 257 | content: " "; /* 1 */ 258 | display: table; /* 2 */ 259 | } 260 | 261 | .clearfix:after { 262 | clear: both; 263 | } 264 | 265 | /* ========================================================================== 266 | EXAMPLE Media Queries for Responsive Design. 267 | These examples override the primary ('mobile first') styles. 268 | Modify as content requires. 269 | ========================================================================== */ 270 | 271 | @media only screen and (min-width: 35em) { 272 | /* Style adjustments for viewports that meet the condition */ 273 | } 274 | 275 | @media print, 276 | (-o-min-device-pixel-ratio: 5/4), 277 | (-webkit-min-device-pixel-ratio: 1.25), 278 | (min-resolution: 120dpi) { 279 | /* Style adjustments for high resolution devices */ 280 | } 281 | 282 | /* ========================================================================== 283 | Print styles. 284 | Inlined to avoid required HTTP connection: h5bp.com/r 285 | ========================================================================== */ 286 | 287 | @media print { 288 | * { 289 | background: transparent !important; 290 | color: #000 !important; /* Black prints faster: h5bp.com/s */ 291 | box-shadow: none !important; 292 | text-shadow: none !important; 293 | } 294 | 295 | a, 296 | a:visited { 297 | text-decoration: underline; 298 | } 299 | 300 | a[href]:after { 301 | content: " (" attr(href) ")"; 302 | } 303 | 304 | abbr[title]:after { 305 | content: " (" attr(title) ")"; 306 | } 307 | 308 | /* 309 | * Don't show links for images, or javascript/internal links 310 | */ 311 | 312 | a[href^="javascript:"]:after, 313 | a[href^="#"]:after { 314 | content: ""; 315 | } 316 | 317 | pre, 318 | blockquote { 319 | border: 1px solid #999; 320 | page-break-inside: avoid; 321 | } 322 | 323 | thead { 324 | display: table-header-group; /* h5bp.com/t */ 325 | } 326 | 327 | tr, 328 | img { 329 | page-break-inside: avoid; 330 | } 331 | 332 | img { 333 | max-width: 100% !important; 334 | } 335 | 336 | @page { 337 | margin: 0.5cm; 338 | } 339 | 340 | p, 341 | h2, 342 | h3 { 343 | orphans: 3; 344 | widows: 3; 345 | } 346 | 347 | h2, 348 | h3 { 349 | page-break-after: avoid; 350 | } 351 | } -------------------------------------------------------------------------------- /classifier.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Contains main implementation of a generic Classifier for tweets. 3 | Reads in language model and stores into a model. 4 | ''' 5 | import csv 6 | import re 7 | import nltk 8 | import os 9 | import cPickle as pickle 10 | import random 11 | 12 | class Classifier: 13 | ''' 14 | rawfname -> name of file containing raw training data 15 | force == True iff user wants to overwrite classifier data 16 | grams -> list of n-grams to use: 17 | 1, unigrams; 2, bigrams; 3, trigrams; and so on 18 | so [1,2] means unigrams + bigrams 19 | __init__(self, rawfname, modelfname, force, grams) 20 | ''' 21 | def __init__(self, rawfname, *args, **kargs): 22 | self.rawfname = rawfname 23 | 24 | self.force = kargs.get("force", False) 25 | self.numgrams = kargs.get("grams", [1]) 26 | 27 | # create modelfname using numgrams variable 28 | # e.g if self.numgrams = [1,2], then 29 | # self.modelfname = 'model1-2.dat' 30 | self.modelfname = "model%s.dat" % \ 31 | (reduce(lambda x, y: str(x)+'-'+str(y), 32 | self.numgrams)) 33 | 34 | # weight to use in self.weightedProb 35 | self.weight = kargs.get("weight", 0.00005) 36 | 37 | # The number of lines to train on. Use during development 38 | # to train on only a small chunk of the training set 39 | self.filesubset = kargs.get("filesubset", "all") 40 | 41 | # counts of tweets in each class 42 | # [x,y] where 43 | # x -> number of tweets in negative class 44 | # y -> number of tweets in positive class 45 | self.tweetcounts = [0, 0] 46 | 47 | # counts of feature/class combinations 48 | # stores (feature) => [x, y] where 49 | # x -> number of times feature appears in negative class 50 | # y -> number of times feature appears in positive class 51 | self.ftweetcounts = {} 52 | 53 | def incFC(self, f, c): 54 | ''' 55 | Increment count of a feature/class pair 56 | ''' 57 | self.ftweetcounts.setdefault(f, [0, 0]) 58 | self.ftweetcounts[f][c] += 1 59 | 60 | def incC(self, c): 61 | ''' 62 | Increment count of a class 63 | ''' 64 | self.tweetcounts[c] += 1 65 | 66 | def getFC(self, f, c): 67 | ''' 68 | Return number of times a features has appeared in a class 69 | ''' 70 | if f in self.ftweetcounts: 71 | return float(self.ftweetcounts[f][c]) 72 | return 0.0 73 | 74 | def getC(self, c): 75 | ''' 76 | Return number of features in a class 77 | ''' 78 | return float(self.tweetcounts[c]) 79 | 80 | def getTotal(self): 81 | ''' 82 | Return total number of features 83 | ''' 84 | return sum(self.tweetcounts) 85 | 86 | def getFeatures(self, item): 87 | ''' 88 | Each feature has weight 1 89 | That is, even if the word 'obama' appears >10 times 90 | in a tweet, it is counted only once in that particular tweet 91 | ''' 92 | flist = [] 93 | for gram in self.numgrams: 94 | tokenized = nltk.word_tokenize(item) 95 | for i in range(len(tokenized)-gram+1): 96 | flist.append(" ".join(tokenized[i:i+gram])) 97 | return set(flist) 98 | 99 | def train(self, c, item): 100 | ''' 101 | Trains the classifier using item (for now, just text) on a 102 | specific class 103 | c -> class (number) 104 | ''' 105 | 106 | features = self.getFeatures(item) 107 | for f in features: 108 | self.incFC(f, c) 109 | self.incC(c) 110 | 111 | def trainClassifier(self): 112 | ''' 113 | Trains the classifier based on tweets in self.modelfname 114 | Stores the resulting data structures in a pickle file 115 | ''' 116 | if self.force: 117 | os.remove(self.modelfname) 118 | elif os.path.exists(self.modelfname): 119 | grams, self.tweetcounts, self.ftweetcounts = pickle.load( 120 | open(self.modelfname, "rb") 121 | ) 122 | # stop iff we have data for the number of grams we want 123 | if grams == self.numgrams: 124 | print "Model retrieved from '%s'" % self.modelfname 125 | return 126 | 127 | f = open(self.rawfname) 128 | r = csv.reader(f, delimiter=',', quotechar='"') 129 | 130 | # get 0th column -> '0' if negative (class 0), '4' if positive (class 1) 131 | # get 5th column -> contains text of tweet 132 | stripped = [(0 if line[0] == '0' else 1, 133 | re.sub(r'[,.]', r'', 134 | line[-1].lower().strip())) for line in r] 135 | 136 | # Only train on lines 0 -> of the training set 137 | last_line = len(stripped) if self.filesubset == "all" else self.filesubset 138 | 139 | for each in stripped[:last_line]: 140 | self.train(each[0], each[1]) 141 | 142 | # store Classifier training data 143 | pickle.dump([self.numgrams, self.tweetcounts, self.ftweetcounts], 144 | open(self.modelfname, "wb") 145 | ) 146 | 147 | print "Model stored in '%s'" % self.modelfname 148 | 149 | f.close() 150 | 151 | def getSampleTweets(self, n, pct_pos = .5): 152 | ''' 153 | Return tweets from the training set where of the tweets 154 | have positive sentiment and (1 - ) have negative sentiment 155 | ''' 156 | random.seed(10) 157 | numpos, numneg = 0, 0 158 | targetpos, targetneg = int(n * pct_pos), int(n * (1 - pct_pos)) 159 | 160 | # Should have lines in the end 161 | sample = [] 162 | 163 | f = open(self.rawfname) 164 | r = csv.reader(f, delimiter=',', quotechar='"') 165 | 166 | # get 0th column -> '0' if negative (class 0), '4' if positive (class 1) 167 | # get 5th column -> contains text of tweet 168 | stripped = [(0 if line[0] == '0' else 1, 169 | re.sub(r'[,.]', r'', 170 | line[-1].lower().strip())) for line in r] 171 | 172 | random.shuffle(stripped) 173 | 174 | i = 0 175 | 176 | # Read through the shuffled list of examples until there are 177 | # positive tweets and negative tweets 178 | # in our sample 179 | while numpos < targetpos or numneg < targetneg: 180 | curtweet = stripped[i] 181 | 182 | if curtweet[0] == 0 and numneg < targetneg: 183 | numneg += 1 184 | sample.append(curtweet) 185 | elif curtweet[0] == 1 and numpos < targetpos: 186 | numpos += 1 187 | sample.append(curtweet) 188 | i += 1 189 | 190 | return sample 191 | 192 | def probFC(self, f, c): 193 | ''' 194 | Return the probability of a feature being in a particular class 195 | ''' 196 | if self.getC(c) == 0: 197 | return 0 198 | return self.getFC(f, c)/self.getC(c) 199 | 200 | def probC(self, c): 201 | ''' 202 | Return the probability Prob(Class) 203 | ''' 204 | return self.getC(c)/self.getTotal() 205 | 206 | def setWeight(self, w): 207 | ''' 208 | Set weight to use in classifier 209 | ''' 210 | self.weight = w 211 | 212 | def weightedProb(self, f, c, ap=0.5): 213 | ''' 214 | Method of smoothing: 215 | Start with an assumed probability (ap) for each word in each class 216 | Then, return weighted probability of real probability (probFC) 217 | and assumed probability 218 | weight of 1.0 means ap is weighted as much as a word 219 | Bayesian in nature: 220 | For example, the word 'dude' might not be in the corpus initially. 221 | so assuming weight of 1.0, then 222 | P('dude' | class=0) = 0.5 and P('dude' | class=1) = 0.5 223 | then when we find one 'dude' that's positive, 224 | P('dude' | class=0) = 0.25 and P('dude' | class=1) = 0.75 225 | ''' 226 | # calculate current probability 227 | real = self.probFC(f, c) 228 | 229 | # count number of times this feature has appeared in all categories 230 | totals = sum([self.getFC(f,c) for c in [0, 1]]) 231 | 232 | # calculate weighted average 233 | return ((self.weight * ap) + (totals * real))/(self.weight + totals) 234 | 235 | 236 | def classify(self, text): 237 | ''' 238 | Return 0 if negative; Return 1 if positive 239 | ''' 240 | raise Exception("You must subclass 'Classifier' to classify tweets") 241 | 242 | def __repr__(self): 243 | return "Classifier info: (weight=%s, grams=%s)" % (self.weight, self.numgrams) 244 | -------------------------------------------------------------------------------- /static/css/normalize.css: -------------------------------------------------------------------------------- 1 | /*! normalize.css v2.1.3 | MIT License | git.io/normalize */ 2 | 3 | /* ========================================================================== 4 | HTML5 display definitions 5 | ========================================================================== */ 6 | 7 | /** 8 | * Correct `block` display not defined in IE 8/9. 9 | */ 10 | 11 | article, 12 | aside, 13 | details, 14 | figcaption, 15 | figure, 16 | footer, 17 | header, 18 | hgroup, 19 | main, 20 | nav, 21 | section, 22 | summary { 23 | display: block; 24 | } 25 | 26 | /** 27 | * Correct `inline-block` display not defined in IE 8/9. 28 | */ 29 | 30 | audio, 31 | canvas, 32 | video { 33 | display: inline-block; 34 | } 35 | 36 | /** 37 | * Prevent modern browsers from displaying `audio` without controls. 38 | * Remove excess height in iOS 5 devices. 39 | */ 40 | 41 | audio:not([controls]) { 42 | display: none; 43 | height: 0; 44 | } 45 | 46 | /** 47 | * Address `[hidden]` styling not present in IE 8/9. 48 | * Hide the `template` element in IE, Safari, and Firefox < 22. 49 | */ 50 | 51 | [hidden], 52 | template { 53 | display: none; 54 | } 55 | 56 | /* ========================================================================== 57 | Base 58 | ========================================================================== */ 59 | 60 | /** 61 | * 1. Set default font family to sans-serif. 62 | * 2. Prevent iOS text size adjust after orientation change, without disabling 63 | * user zoom. 64 | */ 65 | 66 | html { 67 | font-family: sans-serif; /* 1 */ 68 | -ms-text-size-adjust: 100%; /* 2 */ 69 | -webkit-text-size-adjust: 100%; /* 2 */ 70 | } 71 | 72 | /** 73 | * Remove default margin. 74 | */ 75 | 76 | body { 77 | margin: 0; 78 | } 79 | 80 | /* ========================================================================== 81 | Links 82 | ========================================================================== */ 83 | 84 | /** 85 | * Remove the gray background color from active links in IE 10. 86 | */ 87 | 88 | a { 89 | background: transparent; 90 | } 91 | 92 | /** 93 | * Address `outline` inconsistency between Chrome and other browsers. 94 | */ 95 | 96 | a:focus { 97 | outline: thin dotted; 98 | } 99 | 100 | /** 101 | * Improve readability when focused and also mouse hovered in all browsers. 102 | */ 103 | 104 | a:active, 105 | a:hover { 106 | outline: 0; 107 | } 108 | 109 | /* ========================================================================== 110 | Typography 111 | ========================================================================== */ 112 | 113 | /** 114 | * Address variable `h1` font-size and margin within `section` and `article` 115 | * contexts in Firefox 4+, Safari 5, and Chrome. 116 | */ 117 | 118 | h1 { 119 | font-size: 2em; 120 | margin: 0.67em 0; 121 | } 122 | 123 | /** 124 | * Address styling not present in IE 8/9, Safari 5, and Chrome. 125 | */ 126 | 127 | abbr[title] { 128 | border-bottom: 1px dotted; 129 | } 130 | 131 | /** 132 | * Address style set to `bolder` in Firefox 4+, Safari 5, and Chrome. 133 | */ 134 | 135 | b, 136 | strong { 137 | font-weight: bold; 138 | } 139 | 140 | /** 141 | * Address styling not present in Safari 5 and Chrome. 142 | */ 143 | 144 | dfn { 145 | font-style: italic; 146 | } 147 | 148 | /** 149 | * Address differences between Firefox and other browsers. 150 | */ 151 | 152 | hr { 153 | -moz-box-sizing: content-box; 154 | box-sizing: content-box; 155 | height: 0; 156 | } 157 | 158 | /** 159 | * Address styling not present in IE 8/9. 160 | */ 161 | 162 | mark { 163 | background: #ff0; 164 | color: #000; 165 | } 166 | 167 | /** 168 | * Correct font family set oddly in Safari 5 and Chrome. 169 | */ 170 | 171 | code, 172 | kbd, 173 | pre, 174 | samp { 175 | font-family: monospace, serif; 176 | font-size: 1em; 177 | } 178 | 179 | /** 180 | * Improve readability of pre-formatted text in all browsers. 181 | */ 182 | 183 | pre { 184 | white-space: pre-wrap; 185 | } 186 | 187 | /** 188 | * Set consistent quote types. 189 | */ 190 | 191 | q { 192 | quotes: "\201C" "\201D" "\2018" "\2019"; 193 | } 194 | 195 | /** 196 | * Address inconsistent and variable font size in all browsers. 197 | */ 198 | 199 | small { 200 | font-size: 80%; 201 | } 202 | 203 | /** 204 | * Prevent `sub` and `sup` affecting `line-height` in all browsers. 205 | */ 206 | 207 | sub, 208 | sup { 209 | font-size: 75%; 210 | line-height: 0; 211 | position: relative; 212 | vertical-align: baseline; 213 | } 214 | 215 | sup { 216 | top: -0.5em; 217 | } 218 | 219 | sub { 220 | bottom: -0.25em; 221 | } 222 | 223 | /* ========================================================================== 224 | Embedded content 225 | ========================================================================== */ 226 | 227 | /** 228 | * Remove border when inside `a` element in IE 8/9. 229 | */ 230 | 231 | img { 232 | border: 0; 233 | } 234 | 235 | /** 236 | * Correct overflow displayed oddly in IE 9. 237 | */ 238 | 239 | svg:not(:root) { 240 | overflow: hidden; 241 | } 242 | 243 | /* ========================================================================== 244 | Figures 245 | ========================================================================== */ 246 | 247 | /** 248 | * Address margin not present in IE 8/9 and Safari 5. 249 | */ 250 | 251 | figure { 252 | margin: 0; 253 | } 254 | 255 | /* ========================================================================== 256 | Forms 257 | ========================================================================== */ 258 | 259 | /** 260 | * Define consistent border, margin, and padding. 261 | */ 262 | 263 | fieldset { 264 | border: 1px solid #c0c0c0; 265 | margin: 0 2px; 266 | padding: 0.35em 0.625em 0.75em; 267 | } 268 | 269 | /** 270 | * 1. Correct `color` not being inherited in IE 8/9. 271 | * 2. Remove padding so people aren't caught out if they zero out fieldsets. 272 | */ 273 | 274 | legend { 275 | border: 0; /* 1 */ 276 | padding: 0; /* 2 */ 277 | } 278 | 279 | /** 280 | * 1. Correct font family not being inherited in all browsers. 281 | * 2. Correct font size not being inherited in all browsers. 282 | * 3. Address margins set differently in Firefox 4+, Safari 5, and Chrome. 283 | */ 284 | 285 | button, 286 | input, 287 | select, 288 | textarea { 289 | font-family: inherit; /* 1 */ 290 | font-size: 100%; /* 2 */ 291 | margin: 0; /* 3 */ 292 | } 293 | 294 | /** 295 | * Address Firefox 4+ setting `line-height` on `input` using `!important` in 296 | * the UA stylesheet. 297 | */ 298 | 299 | button, 300 | input { 301 | line-height: normal; 302 | } 303 | 304 | /** 305 | * Address inconsistent `text-transform` inheritance for `button` and `select`. 306 | * All other form control elements do not inherit `text-transform` values. 307 | * Correct `button` style inheritance in Chrome, Safari 5+, and IE 8+. 308 | * Correct `select` style inheritance in Firefox 4+ and Opera. 309 | */ 310 | 311 | button, 312 | select { 313 | text-transform: none; 314 | } 315 | 316 | /** 317 | * 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio` 318 | * and `video` controls. 319 | * 2. Correct inability to style clickable `input` types in iOS. 320 | * 3. Improve usability and consistency of cursor style between image-type 321 | * `input` and others. 322 | */ 323 | 324 | button, 325 | html input[type="button"], /* 1 */ 326 | input[type="reset"], 327 | input[type="submit"] { 328 | -webkit-appearance: button; /* 2 */ 329 | cursor: pointer; /* 3 */ 330 | } 331 | 332 | /** 333 | * Re-set default cursor for disabled elements. 334 | */ 335 | 336 | button[disabled], 337 | html input[disabled] { 338 | cursor: default; 339 | } 340 | 341 | /** 342 | * 1. Address box sizing set to `content-box` in IE 8/9/10. 343 | * 2. Remove excess padding in IE 8/9/10. 344 | */ 345 | 346 | input[type="checkbox"], 347 | input[type="radio"] { 348 | box-sizing: border-box; /* 1 */ 349 | padding: 0; /* 2 */ 350 | } 351 | 352 | /** 353 | * 1. Address `appearance` set to `searchfield` in Safari 5 and Chrome. 354 | * 2. Address `box-sizing` set to `border-box` in Safari 5 and Chrome 355 | * (include `-moz` to future-proof). 356 | */ 357 | 358 | input[type="search"] { 359 | -webkit-appearance: textfield; /* 1 */ 360 | -moz-box-sizing: content-box; 361 | -webkit-box-sizing: content-box; /* 2 */ 362 | box-sizing: content-box; 363 | } 364 | 365 | /** 366 | * Remove inner padding and search cancel button in Safari 5 and Chrome 367 | * on OS X. 368 | */ 369 | 370 | input[type="search"]::-webkit-search-cancel-button, 371 | input[type="search"]::-webkit-search-decoration { 372 | -webkit-appearance: none; 373 | } 374 | 375 | /** 376 | * Remove inner padding and border in Firefox 4+. 377 | */ 378 | 379 | button::-moz-focus-inner, 380 | input::-moz-focus-inner { 381 | border: 0; 382 | padding: 0; 383 | } 384 | 385 | /** 386 | * 1. Remove default vertical scrollbar in IE 8/9. 387 | * 2. Improve readability and alignment in all browsers. 388 | */ 389 | 390 | textarea { 391 | overflow: auto; /* 1 */ 392 | vertical-align: top; /* 2 */ 393 | } 394 | 395 | /* ========================================================================== 396 | Tables 397 | ========================================================================== */ 398 | 399 | /** 400 | * Remove most spacing between table cells. 401 | */ 402 | 403 | table { 404 | border-collapse: collapse; 405 | border-spacing: 0; 406 | } -------------------------------------------------------------------------------- /static/css/bootstrap.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap v2.0.4 3 | * 4 | * Copyright 2012 Twitter, Inc 5 | * Licensed under the Apache License v2.0 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Designed and built with all the love in the world @twitter by @mdo and @fat. 9 | */article,aside,details,figcaption,figure,footer,header,hgroup,nav,section{display:block}audio,canvas,video{display:inline-block;*display:inline;*zoom:1}audio:not([controls]){display:none}html{font-size:100%;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}a:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}a:hover,a:active{outline:0}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sup{top:-0.5em}sub{bottom:-0.25em}img{max-width:100%;vertical-align:middle;border:0;-ms-interpolation-mode:bicubic}#map_canvas img{max-width:none}button,input,select,textarea{margin:0;font-size:100%;vertical-align:middle}button,input{*overflow:visible;line-height:normal}button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0}button,input[type="button"],input[type="reset"],input[type="submit"]{cursor:pointer;-webkit-appearance:button}input[type="search"]{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;-webkit-appearance:textfield}input[type="search"]::-webkit-search-decoration,input[type="search"]::-webkit-search-cancel-button{-webkit-appearance:none}textarea{overflow:auto;vertical-align:top}.clearfix{*zoom:1}.clearfix:before,.clearfix:after{display:table;content:""}.clearfix:after{clear:both}.hide-text{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.input-block-level{display:block;width:100%;min-height:28px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;-ms-box-sizing:border-box;box-sizing:border-box}body{margin:0;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:13px;line-height:18px;color:#333;background-color:#fff}a{color:#08c;text-decoration:none}a:hover{color:#005580;text-decoration:underline}.row{margin-left:-20px;*zoom:1}.row:before,.row:after{display:table;content:""}.row:after{clear:both}[class*="span"]{float:left;margin-left:20px}.container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:940px}.span12{width:940px}.span11{width:860px}.span10{width:780px}.span9{width:700px}.span8{width:620px}.span7{width:540px}.span6{width:460px}.span5{width:380px}.span4{width:300px}.span3{width:220px}.span2{width:140px}.span1{width:60px}.offset12{margin-left:980px}.offset11{margin-left:900px}.offset10{margin-left:820px}.offset9{margin-left:740px}.offset8{margin-left:660px}.offset7{margin-left:580px}.offset6{margin-left:500px}.offset5{margin-left:420px}.offset4{margin-left:340px}.offset3{margin-left:260px}.offset2{margin-left:180px}.offset1{margin-left:100px}.row-fluid{width:100%;*zoom:1}.row-fluid:before,.row-fluid:after{display:table;content:""}.row-fluid:after{clear:both}.row-fluid [class*="span"]{display:block;float:left;width:100%;min-height:28px;margin-left:2.127659574%;*margin-left:2.0744680846382977%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;-ms-box-sizing:border-box;box-sizing:border-box}.row-fluid [class*="span"]:first-child{margin-left:0}.row-fluid .span12{width:99.99999998999999%;*width:99.94680850063828%}.row-fluid .span11{width:91.489361693%;*width:91.4361702036383%}.row-fluid .span10{width:82.97872339599999%;*width:82.92553190663828%}.row-fluid .span9{width:74.468085099%;*width:74.4148936096383%}.row-fluid .span8{width:65.95744680199999%;*width:65.90425531263828%}.row-fluid .span7{width:57.446808505%;*width:57.3936170156383%}.row-fluid .span6{width:48.93617020799999%;*width:48.88297871863829%}.row-fluid .span5{width:40.425531911%;*width:40.3723404216383%}.row-fluid .span4{width:31.914893614%;*width:31.8617021246383%}.row-fluid .span3{width:23.404255317%;*width:23.3510638276383%}.row-fluid .span2{width:14.89361702%;*width:14.8404255306383%}.row-fluid .span1{width:6.382978723%;*width:6.329787233638298%}.container{margin-right:auto;margin-left:auto;*zoom:1}.container:before,.container:after{display:table;content:""}.container:after{clear:both}.container-fluid{padding-right:20px;padding-left:20px;*zoom:1}.container-fluid:before,.container-fluid:after{display:table;content:""}.container-fluid:after{clear:both}p{margin:0 0 9px}p small{font-size:11px;color:#999}.lead{margin-bottom:18px;font-size:20px;font-weight:200;line-height:27px}h1,h2,h3,h4,h5,h6{margin:0;font-family:inherit;font-weight:bold;color:inherit;text-rendering:optimizelegibility}h1 small,h2 small,h3 small,h4 small,h5 small,h6 small{font-weight:normal;color:#999}h1{font-size:30px;line-height:36px}h1 small{font-size:18px}h2{font-size:24px;line-height:36px}h2 small{font-size:18px}h3{font-size:18px;line-height:27px}h3 small{font-size:14px}h4,h5,h6{line-height:18px}h4{font-size:14px}h4 small{font-size:12px}h5{font-size:12px}h6{font-size:11px;color:#999;text-transform:uppercase}.page-header{padding-bottom:17px;margin:18px 0;border-bottom:1px solid #eee}.page-header h1{line-height:1}ul,ol{padding:0;margin:0 0 9px 25px}ul ul,ul ol,ol ol,ol ul{margin-bottom:0}ul{list-style:disc}ol{list-style:decimal}li{line-height:18px}ul.unstyled,ol.unstyled{margin-left:0;list-style:none}dl{margin-bottom:18px}dt,dd{line-height:18px}dt{font-weight:bold;line-height:17px}dd{margin-left:9px}.dl-horizontal dt{float:left;width:120px;overflow:hidden;clear:left;text-align:right;text-overflow:ellipsis;white-space:nowrap}.dl-horizontal dd{margin-left:130px}hr{margin:18px 0;border:0;border-top:1px solid #eee;border-bottom:1px solid #fff}strong{font-weight:bold}em{font-style:italic}.muted{color:#999}abbr[title]{cursor:help;border-bottom:1px dotted #999}abbr.initialism{font-size:90%;text-transform:uppercase}blockquote{padding:0 0 0 15px;margin:0 0 18px;border-left:5px solid #eee}blockquote p{margin-bottom:0;font-size:16px;font-weight:300;line-height:22.5px}blockquote small{display:block;line-height:18px;color:#999}blockquote small:before{content:'\2014 \00A0'}blockquote.pull-right{float:right;padding-right:15px;padding-left:0;border-right:5px solid #eee;border-left:0}blockquote.pull-right p,blockquote.pull-right small{text-align:right}q:before,q:after,blockquote:before,blockquote:after{content:""}address{display:block;margin-bottom:18px;font-style:normal;line-height:18px}small{font-size:100%}cite{font-style:normal}code,pre{padding:0 3px 2px;font-family:Menlo,Monaco,Consolas,"Courier New",monospace;font-size:12px;color:#333;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}code{padding:2px 4px;color:#d14;background-color:#f7f7f9;border:1px solid #e1e1e8}pre{display:block;padding:8.5px;margin:0 0 9px;font-size:12.025px;line-height:18px;word-break:break-all;word-wrap:break-word;white-space:pre;white-space:pre-wrap;background-color:#f5f5f5;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.15);-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}pre.prettyprint{margin-bottom:18px}pre code{padding:0;color:inherit;background-color:transparent;border:0}.pre-scrollable{max-height:340px;overflow-y:scroll}form{margin:0 0 18px}fieldset{padding:0;margin:0;border:0}legend{display:block;width:100%;padding:0;margin-bottom:27px;font-size:19.5px;line-height:36px;color:#333;border:0;border-bottom:1px solid #e5e5e5}legend small{font-size:13.5px;color:#999}label,input,button,select,textarea{font-size:13px;font-weight:normal;line-height:18px}input,button,select,textarea{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif}label{display:block;margin-bottom:5px}select,textarea,input[type="text"],input[type="password"],input[type="datetime"],input[type="datetime-local"],input[type="date"],input[type="month"],input[type="time"],input[type="week"],input[type="number"],input[type="email"],input[type="url"],input[type="search"],input[type="tel"],input[type="color"],.uneditable-input{display:inline-block;height:18px;padding:4px;margin-bottom:9px;font-size:13px;line-height:18px;color:#555}input,textarea{width:210px}textarea{height:auto}textarea,input[type="text"],input[type="password"],input[type="datetime"],input[type="datetime-local"],input[type="date"],input[type="month"],input[type="time"],input[type="week"],input[type="number"],input[type="email"],input[type="url"],input[type="search"],input[type="tel"],input[type="color"],.uneditable-input{background-color:#fff;border:1px solid #ccc;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-webkit-transition:border linear .2s,box-shadow linear .2s;-moz-transition:border linear .2s,box-shadow linear .2s;-ms-transition:border linear .2s,box-shadow linear .2s;-o-transition:border linear .2s,box-shadow linear .2s;transition:border linear .2s,box-shadow linear .2s}textarea:focus,input[type="text"]:focus,input[type="password"]:focus,input[type="datetime"]:focus,input[type="datetime-local"]:focus,input[type="date"]:focus,input[type="month"]:focus,input[type="time"]:focus,input[type="week"]:focus,input[type="number"]:focus,input[type="email"]:focus,input[type="url"]:focus,input[type="search"]:focus,input[type="tel"]:focus,input[type="color"]:focus,.uneditable-input:focus{border-color:rgba(82,168,236,0.8);outline:0;outline:thin dotted \9;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(82,168,236,0.6);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(82,168,236,0.6);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(82,168,236,0.6)}input[type="radio"],input[type="checkbox"]{margin:3px 0;*margin-top:0;line-height:normal;cursor:pointer}input[type="submit"],input[type="reset"],input[type="button"],input[type="radio"],input[type="checkbox"]{width:auto}.uneditable-textarea{width:auto;height:auto}select,input[type="file"]{height:28px;*margin-top:4px;line-height:28px}select{width:220px;border:1px solid #bbb}select[multiple],select[size]{height:auto}select:focus,input[type="file"]:focus,input[type="radio"]:focus,input[type="checkbox"]:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.radio,.checkbox{min-height:18px;padding-left:18px}.radio input[type="radio"],.checkbox input[type="checkbox"]{float:left;margin-left:-18px}.controls>.radio:first-child,.controls>.checkbox:first-child{padding-top:5px}.radio.inline,.checkbox.inline{display:inline-block;padding-top:5px;margin-bottom:0;vertical-align:middle}.radio.inline+.radio.inline,.checkbox.inline+.checkbox.inline{margin-left:10px}.input-mini{width:60px}.input-small{width:90px}.input-medium{width:150px}.input-large{width:210px}.input-xlarge{width:270px}.input-xxlarge{width:530px}input[class*="span"],select[class*="span"],textarea[class*="span"],.uneditable-input[class*="span"],.row-fluid input[class*="span"],.row-fluid select[class*="span"],.row-fluid textarea[class*="span"],.row-fluid .uneditable-input[class*="span"]{float:none;margin-left:0}.input-append input[class*="span"],.input-append .uneditable-input[class*="span"],.input-prepend input[class*="span"],.input-prepend .uneditable-input[class*="span"],.row-fluid .input-prepend [class*="span"],.row-fluid .input-append [class*="span"]{display:inline-block}input,textarea,.uneditable-input{margin-left:0}input.span12,textarea.span12,.uneditable-input.span12{width:930px}input.span11,textarea.span11,.uneditable-input.span11{width:850px}input.span10,textarea.span10,.uneditable-input.span10{width:770px}input.span9,textarea.span9,.uneditable-input.span9{width:690px}input.span8,textarea.span8,.uneditable-input.span8{width:610px}input.span7,textarea.span7,.uneditable-input.span7{width:530px}input.span6,textarea.span6,.uneditable-input.span6{width:450px}input.span5,textarea.span5,.uneditable-input.span5{width:370px}input.span4,textarea.span4,.uneditable-input.span4{width:290px}input.span3,textarea.span3,.uneditable-input.span3{width:210px}input.span2,textarea.span2,.uneditable-input.span2{width:130px}input.span1,textarea.span1,.uneditable-input.span1{width:50px}input[disabled],select[disabled],textarea[disabled],input[readonly],select[readonly],textarea[readonly]{cursor:not-allowed;background-color:#eee;border-color:#ddd}input[type="radio"][disabled],input[type="checkbox"][disabled],input[type="radio"][readonly],input[type="checkbox"][readonly]{background-color:transparent}.control-group.warning>label,.control-group.warning .help-block,.control-group.warning .help-inline{color:#c09853}.control-group.warning .checkbox,.control-group.warning .radio,.control-group.warning input,.control-group.warning select,.control-group.warning textarea{color:#c09853;border-color:#c09853}.control-group.warning .checkbox:focus,.control-group.warning .radio:focus,.control-group.warning input:focus,.control-group.warning select:focus,.control-group.warning textarea:focus{border-color:#a47e3c;-webkit-box-shadow:0 0 6px #dbc59e;-moz-box-shadow:0 0 6px #dbc59e;box-shadow:0 0 6px #dbc59e}.control-group.warning .input-prepend .add-on,.control-group.warning .input-append .add-on{color:#c09853;background-color:#fcf8e3;border-color:#c09853}.control-group.error>label,.control-group.error .help-block,.control-group.error .help-inline{color:#b94a48}.control-group.error .checkbox,.control-group.error .radio,.control-group.error input,.control-group.error select,.control-group.error textarea{color:#b94a48;border-color:#b94a48}.control-group.error .checkbox:focus,.control-group.error .radio:focus,.control-group.error input:focus,.control-group.error select:focus,.control-group.error textarea:focus{border-color:#953b39;-webkit-box-shadow:0 0 6px #d59392;-moz-box-shadow:0 0 6px #d59392;box-shadow:0 0 6px #d59392}.control-group.error .input-prepend .add-on,.control-group.error .input-append .add-on{color:#b94a48;background-color:#f2dede;border-color:#b94a48}.control-group.success>label,.control-group.success .help-block,.control-group.success .help-inline{color:#468847}.control-group.success .checkbox,.control-group.success .radio,.control-group.success input,.control-group.success select,.control-group.success textarea{color:#468847;border-color:#468847}.control-group.success .checkbox:focus,.control-group.success .radio:focus,.control-group.success input:focus,.control-group.success select:focus,.control-group.success textarea:focus{border-color:#356635;-webkit-box-shadow:0 0 6px #7aba7b;-moz-box-shadow:0 0 6px #7aba7b;box-shadow:0 0 6px #7aba7b}.control-group.success .input-prepend .add-on,.control-group.success .input-append .add-on{color:#468847;background-color:#dff0d8;border-color:#468847}input:focus:required:invalid,textarea:focus:required:invalid,select:focus:required:invalid{color:#b94a48;border-color:#ee5f5b}input:focus:required:invalid:focus,textarea:focus:required:invalid:focus,select:focus:required:invalid:focus{border-color:#e9322d;-webkit-box-shadow:0 0 6px #f8b9b7;-moz-box-shadow:0 0 6px #f8b9b7;box-shadow:0 0 6px #f8b9b7}.form-actions{padding:17px 20px 18px;margin-top:18px;margin-bottom:18px;background-color:#f5f5f5;border-top:1px solid #e5e5e5;*zoom:1}.form-actions:before,.form-actions:after{display:table;content:""}.form-actions:after{clear:both}.uneditable-input{overflow:hidden;white-space:nowrap;cursor:not-allowed;background-color:#fff;border-color:#eee;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,0.025);-moz-box-shadow:inset 0 1px 2px rgba(0,0,0,0.025);box-shadow:inset 0 1px 2px rgba(0,0,0,0.025)}:-moz-placeholder{color:#999}:-ms-input-placeholder{color:#999}::-webkit-input-placeholder{color:#999}.help-block,.help-inline{color:#555}.help-block{display:block;margin-bottom:9px}.help-inline{display:inline-block;*display:inline;padding-left:5px;vertical-align:middle;*zoom:1}.input-prepend,.input-append{margin-bottom:5px}.input-prepend input,.input-append input,.input-prepend select,.input-append select,.input-prepend .uneditable-input,.input-append .uneditable-input{position:relative;margin-bottom:0;*margin-left:0;vertical-align:middle;-webkit-border-radius:0 3px 3px 0;-moz-border-radius:0 3px 3px 0;border-radius:0 3px 3px 0}.input-prepend input:focus,.input-append input:focus,.input-prepend select:focus,.input-append select:focus,.input-prepend .uneditable-input:focus,.input-append .uneditable-input:focus{z-index:2}.input-prepend .uneditable-input,.input-append .uneditable-input{border-left-color:#ccc}.input-prepend .add-on,.input-append .add-on{display:inline-block;width:auto;height:18px;min-width:16px;padding:4px 5px;font-weight:normal;line-height:18px;text-align:center;text-shadow:0 1px 0 #fff;vertical-align:middle;background-color:#eee;border:1px solid #ccc}.input-prepend .add-on,.input-append .add-on,.input-prepend .btn,.input-append .btn{margin-left:-1px;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.input-prepend .active,.input-append .active{background-color:#a9dba9;border-color:#46a546}.input-prepend .add-on,.input-prepend .btn{margin-right:-1px}.input-prepend .add-on:first-child,.input-prepend .btn:first-child{-webkit-border-radius:3px 0 0 3px;-moz-border-radius:3px 0 0 3px;border-radius:3px 0 0 3px}.input-append input,.input-append select,.input-append .uneditable-input{-webkit-border-radius:3px 0 0 3px;-moz-border-radius:3px 0 0 3px;border-radius:3px 0 0 3px}.input-append .uneditable-input{border-right-color:#ccc;border-left-color:#eee}.input-append .add-on:last-child,.input-append .btn:last-child{-webkit-border-radius:0 3px 3px 0;-moz-border-radius:0 3px 3px 0;border-radius:0 3px 3px 0}.input-prepend.input-append input,.input-prepend.input-append select,.input-prepend.input-append .uneditable-input{-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.input-prepend.input-append .add-on:first-child,.input-prepend.input-append .btn:first-child{margin-right:-1px;-webkit-border-radius:3px 0 0 3px;-moz-border-radius:3px 0 0 3px;border-radius:3px 0 0 3px}.input-prepend.input-append .add-on:last-child,.input-prepend.input-append .btn:last-child{margin-left:-1px;-webkit-border-radius:0 3px 3px 0;-moz-border-radius:0 3px 3px 0;border-radius:0 3px 3px 0}.search-query{padding-right:14px;padding-right:4px \9;padding-left:14px;padding-left:4px \9;margin-bottom:0;-webkit-border-radius:14px;-moz-border-radius:14px;border-radius:14px}.form-search input,.form-inline input,.form-horizontal input,.form-search textarea,.form-inline textarea,.form-horizontal textarea,.form-search select,.form-inline select,.form-horizontal select,.form-search .help-inline,.form-inline .help-inline,.form-horizontal .help-inline,.form-search .uneditable-input,.form-inline .uneditable-input,.form-horizontal .uneditable-input,.form-search .input-prepend,.form-inline .input-prepend,.form-horizontal .input-prepend,.form-search .input-append,.form-inline .input-append,.form-horizontal .input-append{display:inline-block;*display:inline;margin-bottom:0;*zoom:1}.form-search .hide,.form-inline .hide,.form-horizontal .hide{display:none}.form-search label,.form-inline label{display:inline-block}.form-search .input-append,.form-inline .input-append,.form-search .input-prepend,.form-inline .input-prepend{margin-bottom:0}.form-search .radio,.form-search .checkbox,.form-inline .radio,.form-inline .checkbox{padding-left:0;margin-bottom:0;vertical-align:middle}.form-search .radio input[type="radio"],.form-search .checkbox input[type="checkbox"],.form-inline .radio input[type="radio"],.form-inline .checkbox input[type="checkbox"]{float:left;margin-right:3px;margin-left:0}.control-group{margin-bottom:9px}legend+.control-group{margin-top:18px;-webkit-margin-top-collapse:separate}.form-horizontal .control-group{margin-bottom:18px;*zoom:1}.form-horizontal .control-group:before,.form-horizontal .control-group:after{display:table;content:""}.form-horizontal .control-group:after{clear:both}.form-horizontal .control-label{float:left;width:140px;padding-top:5px;text-align:right}.form-horizontal .controls{*display:inline-block;*padding-left:20px;margin-left:160px;*margin-left:0}.form-horizontal .controls:first-child{*padding-left:160px}.form-horizontal .help-block{margin-top:9px;margin-bottom:0}.form-horizontal .form-actions{padding-left:160px}table{max-width:100%;background-color:transparent;border-collapse:collapse;border-spacing:0}.table{width:100%;margin-bottom:18px}.table th,.table td{padding:8px;line-height:18px;text-align:left;vertical-align:top;border-top:1px solid #ddd}.table th{font-weight:bold}.table thead th{vertical-align:bottom}.table caption+thead tr:first-child th,.table caption+thead tr:first-child td,.table colgroup+thead tr:first-child th,.table colgroup+thead tr:first-child td,.table thead:first-child tr:first-child th,.table thead:first-child tr:first-child td{border-top:0}.table tbody+tbody{border-top:2px solid #ddd}.table-condensed th,.table-condensed td{padding:4px 5px}.table-bordered{border:1px solid #ddd;border-collapse:separate;*border-collapse:collapsed;border-left:0;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.table-bordered th,.table-bordered td{border-left:1px solid #ddd}.table-bordered caption+thead tr:first-child th,.table-bordered caption+tbody tr:first-child th,.table-bordered caption+tbody tr:first-child td,.table-bordered colgroup+thead tr:first-child th,.table-bordered colgroup+tbody tr:first-child th,.table-bordered colgroup+tbody tr:first-child td,.table-bordered thead:first-child tr:first-child th,.table-bordered tbody:first-child tr:first-child th,.table-bordered tbody:first-child tr:first-child td{border-top:0}.table-bordered thead:first-child tr:first-child th:first-child,.table-bordered tbody:first-child tr:first-child td:first-child{-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-topleft:4px}.table-bordered thead:first-child tr:first-child th:last-child,.table-bordered tbody:first-child tr:first-child td:last-child{-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-moz-border-radius-topright:4px}.table-bordered thead:last-child tr:last-child th:first-child,.table-bordered tbody:last-child tr:last-child td:first-child{-webkit-border-radius:0 0 0 4px;-moz-border-radius:0 0 0 4px;border-radius:0 0 0 4px;-webkit-border-bottom-left-radius:4px;border-bottom-left-radius:4px;-moz-border-radius-bottomleft:4px}.table-bordered thead:last-child tr:last-child th:last-child,.table-bordered tbody:last-child tr:last-child td:last-child{-webkit-border-bottom-right-radius:4px;border-bottom-right-radius:4px;-moz-border-radius-bottomright:4px}.table-striped tbody tr:nth-child(odd) td,.table-striped tbody tr:nth-child(odd) th{background-color:#f9f9f9}.table tbody tr:hover td,.table tbody tr:hover th{background-color:#f5f5f5}table .span1{float:none;width:44px;margin-left:0}table .span2{float:none;width:124px;margin-left:0}table .span3{float:none;width:204px;margin-left:0}table .span4{float:none;width:284px;margin-left:0}table .span5{float:none;width:364px;margin-left:0}table .span6{float:none;width:444px;margin-left:0}table .span7{float:none;width:524px;margin-left:0}table .span8{float:none;width:604px;margin-left:0}table .span9{float:none;width:684px;margin-left:0}table .span10{float:none;width:764px;margin-left:0}table .span11{float:none;width:844px;margin-left:0}table .span12{float:none;width:924px;margin-left:0}table .span13{float:none;width:1004px;margin-left:0}table .span14{float:none;width:1084px;margin-left:0}table .span15{float:none;width:1164px;margin-left:0}table .span16{float:none;width:1244px;margin-left:0}table .span17{float:none;width:1324px;margin-left:0}table .span18{float:none;width:1404px;margin-left:0}table .span19{float:none;width:1484px;margin-left:0}table .span20{float:none;width:1564px;margin-left:0}table .span21{float:none;width:1644px;margin-left:0}table .span22{float:none;width:1724px;margin-left:0}table .span23{float:none;width:1804px;margin-left:0}table .span24{float:none;width:1884px;margin-left:0}[class^="icon-"],[class*=" icon-"]{display:inline-block;width:14px;height:14px;*margin-right:.3em;line-height:14px;vertical-align:text-top;background-image:url("../img/glyphicons-halflings.png");background-position:14px 14px;background-repeat:no-repeat}[class^="icon-"]:last-child,[class*=" icon-"]:last-child{*margin-left:0}.icon-white{background-image:url("../img/glyphicons-halflings-white.png")}.icon-glass{background-position:0 0}.icon-music{background-position:-24px 0}.icon-search{background-position:-48px 0}.icon-envelope{background-position:-72px 0}.icon-heart{background-position:-96px 0}.icon-star{background-position:-120px 0}.icon-star-empty{background-position:-144px 0}.icon-user{background-position:-168px 0}.icon-film{background-position:-192px 0}.icon-th-large{background-position:-216px 0}.icon-th{background-position:-240px 0}.icon-th-list{background-position:-264px 0}.icon-ok{background-position:-288px 0}.icon-remove{background-position:-312px 0}.icon-zoom-in{background-position:-336px 0}.icon-zoom-out{background-position:-360px 0}.icon-off{background-position:-384px 0}.icon-signal{background-position:-408px 0}.icon-cog{background-position:-432px 0}.icon-trash{background-position:-456px 0}.icon-home{background-position:0 -24px}.icon-file{background-position:-24px -24px}.icon-time{background-position:-48px -24px}.icon-road{background-position:-72px -24px}.icon-download-alt{background-position:-96px -24px}.icon-download{background-position:-120px -24px}.icon-upload{background-position:-144px -24px}.icon-inbox{background-position:-168px -24px}.icon-play-circle{background-position:-192px -24px}.icon-repeat{background-position:-216px -24px}.icon-refresh{background-position:-240px -24px}.icon-list-alt{background-position:-264px -24px}.icon-lock{background-position:-287px -24px}.icon-flag{background-position:-312px -24px}.icon-headphones{background-position:-336px -24px}.icon-volume-off{background-position:-360px -24px}.icon-volume-down{background-position:-384px -24px}.icon-volume-up{background-position:-408px -24px}.icon-qrcode{background-position:-432px -24px}.icon-barcode{background-position:-456px -24px}.icon-tag{background-position:0 -48px}.icon-tags{background-position:-25px -48px}.icon-book{background-position:-48px -48px}.icon-bookmark{background-position:-72px -48px}.icon-print{background-position:-96px -48px}.icon-camera{background-position:-120px -48px}.icon-font{background-position:-144px -48px}.icon-bold{background-position:-167px -48px}.icon-italic{background-position:-192px -48px}.icon-text-height{background-position:-216px -48px}.icon-text-width{background-position:-240px -48px}.icon-align-left{background-position:-264px -48px}.icon-align-center{background-position:-288px -48px}.icon-align-right{background-position:-312px -48px}.icon-align-justify{background-position:-336px -48px}.icon-list{background-position:-360px -48px}.icon-indent-left{background-position:-384px -48px}.icon-indent-right{background-position:-408px -48px}.icon-facetime-video{background-position:-432px -48px}.icon-picture{background-position:-456px -48px}.icon-pencil{background-position:0 -72px}.icon-map-marker{background-position:-24px -72px}.icon-adjust{background-position:-48px -72px}.icon-tint{background-position:-72px -72px}.icon-edit{background-position:-96px -72px}.icon-share{background-position:-120px -72px}.icon-check{background-position:-144px -72px}.icon-move{background-position:-168px -72px}.icon-step-backward{background-position:-192px -72px}.icon-fast-backward{background-position:-216px -72px}.icon-backward{background-position:-240px -72px}.icon-play{background-position:-264px -72px}.icon-pause{background-position:-288px -72px}.icon-stop{background-position:-312px -72px}.icon-forward{background-position:-336px -72px}.icon-fast-forward{background-position:-360px -72px}.icon-step-forward{background-position:-384px -72px}.icon-eject{background-position:-408px -72px}.icon-chevron-left{background-position:-432px -72px}.icon-chevron-right{background-position:-456px -72px}.icon-plus-sign{background-position:0 -96px}.icon-minus-sign{background-position:-24px -96px}.icon-remove-sign{background-position:-48px -96px}.icon-ok-sign{background-position:-72px -96px}.icon-question-sign{background-position:-96px -96px}.icon-info-sign{background-position:-120px -96px}.icon-screenshot{background-position:-144px -96px}.icon-remove-circle{background-position:-168px -96px}.icon-ok-circle{background-position:-192px -96px}.icon-ban-circle{background-position:-216px -96px}.icon-arrow-left{background-position:-240px -96px}.icon-arrow-right{background-position:-264px -96px}.icon-arrow-up{background-position:-289px -96px}.icon-arrow-down{background-position:-312px -96px}.icon-share-alt{background-position:-336px -96px}.icon-resize-full{background-position:-360px -96px}.icon-resize-small{background-position:-384px -96px}.icon-plus{background-position:-408px -96px}.icon-minus{background-position:-433px -96px}.icon-asterisk{background-position:-456px -96px}.icon-exclamation-sign{background-position:0 -120px}.icon-gift{background-position:-24px -120px}.icon-leaf{background-position:-48px -120px}.icon-fire{background-position:-72px -120px}.icon-eye-open{background-position:-96px -120px}.icon-eye-close{background-position:-120px -120px}.icon-warning-sign{background-position:-144px -120px}.icon-plane{background-position:-168px -120px}.icon-calendar{background-position:-192px -120px}.icon-random{background-position:-216px -120px}.icon-comment{background-position:-240px -120px}.icon-magnet{background-position:-264px -120px}.icon-chevron-up{background-position:-288px -120px}.icon-chevron-down{background-position:-313px -119px}.icon-retweet{background-position:-336px -120px}.icon-shopping-cart{background-position:-360px -120px}.icon-folder-close{background-position:-384px -120px}.icon-folder-open{background-position:-408px -120px}.icon-resize-vertical{background-position:-432px -119px}.icon-resize-horizontal{background-position:-456px -118px}.icon-hdd{background-position:0 -144px}.icon-bullhorn{background-position:-24px -144px}.icon-bell{background-position:-48px -144px}.icon-certificate{background-position:-72px -144px}.icon-thumbs-up{background-position:-96px -144px}.icon-thumbs-down{background-position:-120px -144px}.icon-hand-right{background-position:-144px -144px}.icon-hand-left{background-position:-168px -144px}.icon-hand-up{background-position:-192px -144px}.icon-hand-down{background-position:-216px -144px}.icon-circle-arrow-right{background-position:-240px -144px}.icon-circle-arrow-left{background-position:-264px -144px}.icon-circle-arrow-up{background-position:-288px -144px}.icon-circle-arrow-down{background-position:-312px -144px}.icon-globe{background-position:-336px -144px}.icon-wrench{background-position:-360px -144px}.icon-tasks{background-position:-384px -144px}.icon-filter{background-position:-408px -144px}.icon-briefcase{background-position:-432px -144px}.icon-fullscreen{background-position:-456px -144px}.dropup,.dropdown{position:relative}.dropdown-toggle{*margin-bottom:-3px}.dropdown-toggle:active,.open .dropdown-toggle{outline:0}.caret{display:inline-block;width:0;height:0;vertical-align:top;border-top:4px solid #000;border-right:4px solid transparent;border-left:4px solid transparent;content:"";opacity:.3;filter:alpha(opacity=30)}.dropdown .caret{margin-top:8px;margin-left:2px}.dropdown:hover .caret,.open .caret{opacity:1;filter:alpha(opacity=100)}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:4px 0;margin:1px 0 0;list-style:none;background-color:#fff;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.2);*border-right-width:2px;*border-bottom-width:2px;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,0.2);-moz-box-shadow:0 5px 10px rgba(0,0,0,0.2);box-shadow:0 5px 10px rgba(0,0,0,0.2);-webkit-background-clip:padding-box;-moz-background-clip:padding;background-clip:padding-box}.dropdown-menu.pull-right{right:0;left:auto}.dropdown-menu .divider{*width:100%;height:1px;margin:8px 1px;*margin:-5px 0 5px;overflow:hidden;background-color:#e5e5e5;border-bottom:1px solid #fff}.dropdown-menu a{display:block;padding:3px 15px;clear:both;font-weight:normal;line-height:18px;color:#333;white-space:nowrap}.dropdown-menu li>a:hover,.dropdown-menu .active>a,.dropdown-menu .active>a:hover{color:#fff;text-decoration:none;background-color:#08c}.open{*z-index:1000}.open>.dropdown-menu{display:block}.pull-right>.dropdown-menu{right:0;left:auto}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{border-top:0;border-bottom:4px solid #000;content:"\2191"}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:1px}.typeahead{margin-top:2px;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f5f5f5;border:1px solid #eee;border:1px solid rgba(0,0,0,0.05);-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.05);box-shadow:inset 0 1px 1px rgba(0,0,0,0.05)}.well blockquote{border-color:#ddd;border-color:rgba(0,0,0,0.15)}.well-large{padding:24px;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px}.well-small{padding:9px;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.fade{opacity:0;-webkit-transition:opacity .15s linear;-moz-transition:opacity .15s linear;-ms-transition:opacity .15s linear;-o-transition:opacity .15s linear;transition:opacity .15s linear}.fade.in{opacity:1}.collapse{position:relative;height:0;overflow:hidden;-webkit-transition:height .35s ease;-moz-transition:height .35s ease;-ms-transition:height .35s ease;-o-transition:height .35s ease;transition:height .35s ease}.collapse.in{height:auto}.close{float:right;font-size:20px;font-weight:bold;line-height:18px;color:#000;text-shadow:0 1px 0 #fff;opacity:.2;filter:alpha(opacity=20)}.close:hover{color:#000;text-decoration:none;cursor:pointer;opacity:.4;filter:alpha(opacity=40)}button.close{padding:0;cursor:pointer;background:transparent;border:0;-webkit-appearance:none}.btn{display:inline-block;*display:inline;padding:4px 10px 4px;margin-bottom:0;*margin-left:.3em;font-size:13px;line-height:18px;*line-height:20px;color:#333;text-align:center;text-shadow:0 1px 1px rgba(255,255,255,0.75);vertical-align:middle;cursor:pointer;background-color:#f5f5f5;*background-color:#e6e6e6;background-image:-ms-linear-gradient(top,#fff,#e6e6e6);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fff),to(#e6e6e6));background-image:-webkit-linear-gradient(top,#fff,#e6e6e6);background-image:-o-linear-gradient(top,#fff,#e6e6e6);background-image:linear-gradient(top,#fff,#e6e6e6);background-image:-moz-linear-gradient(top,#fff,#e6e6e6);background-repeat:repeat-x;border:1px solid #ccc;*border:0;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);border-color:#e6e6e6 #e6e6e6 #bfbfbf;border-bottom-color:#b3b3b3;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;filter:progid:dximagetransform.microsoft.gradient(startColorstr='#ffffff',endColorstr='#e6e6e6',GradientType=0);filter:progid:dximagetransform.microsoft.gradient(enabled=false);*zoom:1;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05)}.btn:hover,.btn:active,.btn.active,.btn.disabled,.btn[disabled]{background-color:#e6e6e6;*background-color:#d9d9d9}.btn:active,.btn.active{background-color:#ccc \9}.btn:first-child{*margin-left:0}.btn:hover{color:#333;text-decoration:none;background-color:#e6e6e6;*background-color:#d9d9d9;background-position:0 -15px;-webkit-transition:background-position .1s linear;-moz-transition:background-position .1s linear;-ms-transition:background-position .1s linear;-o-transition:background-position .1s linear;transition:background-position .1s linear}.btn:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn.active,.btn:active{background-color:#e6e6e6;background-color:#d9d9d9 \9;background-image:none;outline:0;-webkit-box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05)}.btn.disabled,.btn[disabled]{cursor:default;background-color:#e6e6e6;background-image:none;opacity:.65;filter:alpha(opacity=65);-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.btn-large{padding:9px 14px;font-size:15px;line-height:normal;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px}.btn-large [class^="icon-"]{margin-top:1px}.btn-small{padding:5px 9px;font-size:11px;line-height:16px}.btn-small [class^="icon-"]{margin-top:-1px}.btn-mini{padding:2px 6px;font-size:11px;line-height:14px}.btn-primary,.btn-primary:hover,.btn-warning,.btn-warning:hover,.btn-danger,.btn-danger:hover,.btn-success,.btn-success:hover,.btn-info,.btn-info:hover,.btn-inverse,.btn-inverse:hover{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25)}.btn-primary.active,.btn-warning.active,.btn-danger.active,.btn-success.active,.btn-info.active,.btn-inverse.active{color:rgba(255,255,255,0.75)}.btn{border-color:#ccc;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25)}.btn-primary{background-color:#0074cc;*background-color:#05c;background-image:-ms-linear-gradient(top,#08c,#05c);background-image:-webkit-gradient(linear,0 0,0 100%,from(#08c),to(#05c));background-image:-webkit-linear-gradient(top,#08c,#05c);background-image:-o-linear-gradient(top,#08c,#05c);background-image:-moz-linear-gradient(top,#08c,#05c);background-image:linear-gradient(top,#08c,#05c);background-repeat:repeat-x;border-color:#05c #05c #003580;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:dximagetransform.microsoft.gradient(startColorstr='#0088cc',endColorstr='#0055cc',GradientType=0);filter:progid:dximagetransform.microsoft.gradient(enabled=false)}.btn-primary:hover,.btn-primary:active,.btn-primary.active,.btn-primary.disabled,.btn-primary[disabled]{background-color:#05c;*background-color:#004ab3}.btn-primary:active,.btn-primary.active{background-color:#004099 \9}.btn-warning{background-color:#faa732;*background-color:#f89406;background-image:-ms-linear-gradient(top,#fbb450,#f89406);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fbb450),to(#f89406));background-image:-webkit-linear-gradient(top,#fbb450,#f89406);background-image:-o-linear-gradient(top,#fbb450,#f89406);background-image:-moz-linear-gradient(top,#fbb450,#f89406);background-image:linear-gradient(top,#fbb450,#f89406);background-repeat:repeat-x;border-color:#f89406 #f89406 #ad6704;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:dximagetransform.microsoft.gradient(startColorstr='#fbb450',endColorstr='#f89406',GradientType=0);filter:progid:dximagetransform.microsoft.gradient(enabled=false)}.btn-warning:hover,.btn-warning:active,.btn-warning.active,.btn-warning.disabled,.btn-warning[disabled]{background-color:#f89406;*background-color:#df8505}.btn-warning:active,.btn-warning.active{background-color:#c67605 \9}.btn-danger{background-color:#da4f49;*background-color:#bd362f;background-image:-ms-linear-gradient(top,#ee5f5b,#bd362f);background-image:-webkit-gradient(linear,0 0,0 100%,from(#ee5f5b),to(#bd362f));background-image:-webkit-linear-gradient(top,#ee5f5b,#bd362f);background-image:-o-linear-gradient(top,#ee5f5b,#bd362f);background-image:-moz-linear-gradient(top,#ee5f5b,#bd362f);background-image:linear-gradient(top,#ee5f5b,#bd362f);background-repeat:repeat-x;border-color:#bd362f #bd362f #802420;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:dximagetransform.microsoft.gradient(startColorstr='#ee5f5b',endColorstr='#bd362f',GradientType=0);filter:progid:dximagetransform.microsoft.gradient(enabled=false)}.btn-danger:hover,.btn-danger:active,.btn-danger.active,.btn-danger.disabled,.btn-danger[disabled]{background-color:#bd362f;*background-color:#a9302a}.btn-danger:active,.btn-danger.active{background-color:#942a25 \9}.btn-success{background-color:#5bb75b;*background-color:#51a351;background-image:-ms-linear-gradient(top,#62c462,#51a351);background-image:-webkit-gradient(linear,0 0,0 100%,from(#62c462),to(#51a351));background-image:-webkit-linear-gradient(top,#62c462,#51a351);background-image:-o-linear-gradient(top,#62c462,#51a351);background-image:-moz-linear-gradient(top,#62c462,#51a351);background-image:linear-gradient(top,#62c462,#51a351);background-repeat:repeat-x;border-color:#51a351 #51a351 #387038;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:dximagetransform.microsoft.gradient(startColorstr='#62c462',endColorstr='#51a351',GradientType=0);filter:progid:dximagetransform.microsoft.gradient(enabled=false)}.btn-success:hover,.btn-success:active,.btn-success.active,.btn-success.disabled,.btn-success[disabled]{background-color:#51a351;*background-color:#499249}.btn-success:active,.btn-success.active{background-color:#408140 \9}.btn-info{background-color:#49afcd;*background-color:#2f96b4;background-image:-ms-linear-gradient(top,#5bc0de,#2f96b4);background-image:-webkit-gradient(linear,0 0,0 100%,from(#5bc0de),to(#2f96b4));background-image:-webkit-linear-gradient(top,#5bc0de,#2f96b4);background-image:-o-linear-gradient(top,#5bc0de,#2f96b4);background-image:-moz-linear-gradient(top,#5bc0de,#2f96b4);background-image:linear-gradient(top,#5bc0de,#2f96b4);background-repeat:repeat-x;border-color:#2f96b4 #2f96b4 #1f6377;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:dximagetransform.microsoft.gradient(startColorstr='#5bc0de',endColorstr='#2f96b4',GradientType=0);filter:progid:dximagetransform.microsoft.gradient(enabled=false)}.btn-info:hover,.btn-info:active,.btn-info.active,.btn-info.disabled,.btn-info[disabled]{background-color:#2f96b4;*background-color:#2a85a0}.btn-info:active,.btn-info.active{background-color:#24748c \9}.btn-inverse{background-color:#414141;*background-color:#222;background-image:-ms-linear-gradient(top,#555,#222);background-image:-webkit-gradient(linear,0 0,0 100%,from(#555),to(#222));background-image:-webkit-linear-gradient(top,#555,#222);background-image:-o-linear-gradient(top,#555,#222);background-image:-moz-linear-gradient(top,#555,#222);background-image:linear-gradient(top,#555,#222);background-repeat:repeat-x;border-color:#222 #222 #000;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:dximagetransform.microsoft.gradient(startColorstr='#555555',endColorstr='#222222',GradientType=0);filter:progid:dximagetransform.microsoft.gradient(enabled=false)}.btn-inverse:hover,.btn-inverse:active,.btn-inverse.active,.btn-inverse.disabled,.btn-inverse[disabled]{background-color:#222;*background-color:#151515}.btn-inverse:active,.btn-inverse.active{background-color:#080808 \9}button.btn,input[type="submit"].btn{*padding-top:2px;*padding-bottom:2px}button.btn::-moz-focus-inner,input[type="submit"].btn::-moz-focus-inner{padding:0;border:0}button.btn.btn-large,input[type="submit"].btn.btn-large{*padding-top:7px;*padding-bottom:7px}button.btn.btn-small,input[type="submit"].btn.btn-small{*padding-top:3px;*padding-bottom:3px}button.btn.btn-mini,input[type="submit"].btn.btn-mini{*padding-top:1px;*padding-bottom:1px}.btn-group{position:relative;*margin-left:.3em;*zoom:1}.btn-group:before,.btn-group:after{display:table;content:""}.btn-group:after{clear:both}.btn-group:first-child{*margin-left:0}.btn-group+.btn-group{margin-left:5px}.btn-toolbar{margin-top:9px;margin-bottom:9px}.btn-toolbar .btn-group{display:inline-block;*display:inline;*zoom:1}.btn-group>.btn{position:relative;float:left;margin-left:-1px;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.btn-group>.btn:first-child{margin-left:0;-webkit-border-bottom-left-radius:4px;border-bottom-left-radius:4px;-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-bottomleft:4px;-moz-border-radius-topleft:4px}.btn-group>.btn:last-child,.btn-group>.dropdown-toggle{-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-webkit-border-bottom-right-radius:4px;border-bottom-right-radius:4px;-moz-border-radius-topright:4px;-moz-border-radius-bottomright:4px}.btn-group>.btn.large:first-child{margin-left:0;-webkit-border-bottom-left-radius:6px;border-bottom-left-radius:6px;-webkit-border-top-left-radius:6px;border-top-left-radius:6px;-moz-border-radius-bottomleft:6px;-moz-border-radius-topleft:6px}.btn-group>.btn.large:last-child,.btn-group>.large.dropdown-toggle{-webkit-border-top-right-radius:6px;border-top-right-radius:6px;-webkit-border-bottom-right-radius:6px;border-bottom-right-radius:6px;-moz-border-radius-topright:6px;-moz-border-radius-bottomright:6px}.btn-group>.btn:hover,.btn-group>.btn:focus,.btn-group>.btn:active,.btn-group>.btn.active{z-index:2}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group>.dropdown-toggle{*padding-top:4px;padding-right:8px;*padding-bottom:4px;padding-left:8px;-webkit-box-shadow:inset 1px 0 0 rgba(255,255,255,0.125),inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 1px 0 0 rgba(255,255,255,0.125),inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 1px 0 0 rgba(255,255,255,0.125),inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05)}.btn-group>.btn-mini.dropdown-toggle{padding-right:5px;padding-left:5px}.btn-group>.btn-small.dropdown-toggle{*padding-top:4px;*padding-bottom:4px}.btn-group>.btn-large.dropdown-toggle{padding-right:12px;padding-left:12px}.btn-group.open .dropdown-toggle{background-image:none;-webkit-box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05)}.btn-group.open .btn.dropdown-toggle{background-color:#e6e6e6}.btn-group.open .btn-primary.dropdown-toggle{background-color:#05c}.btn-group.open .btn-warning.dropdown-toggle{background-color:#f89406}.btn-group.open .btn-danger.dropdown-toggle{background-color:#bd362f}.btn-group.open .btn-success.dropdown-toggle{background-color:#51a351}.btn-group.open .btn-info.dropdown-toggle{background-color:#2f96b4}.btn-group.open .btn-inverse.dropdown-toggle{background-color:#222}.btn .caret{margin-top:7px;margin-left:0}.btn:hover .caret,.open.btn-group .caret{opacity:1;filter:alpha(opacity=100)}.btn-mini .caret{margin-top:5px}.btn-small .caret{margin-top:6px}.btn-large .caret{margin-top:6px;border-top-width:5px;border-right-width:5px;border-left-width:5px}.dropup .btn-large .caret{border-top:0;border-bottom:5px solid #000}.btn-primary .caret,.btn-warning .caret,.btn-danger .caret,.btn-info .caret,.btn-success .caret,.btn-inverse .caret{border-top-color:#fff;border-bottom-color:#fff;opacity:.75;filter:alpha(opacity=75)}.alert{padding:8px 35px 8px 14px;margin-bottom:18px;color:#c09853;text-shadow:0 1px 0 rgba(255,255,255,0.5);background-color:#fcf8e3;border:1px solid #fbeed5;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.alert-heading{color:inherit}.alert .close{position:relative;top:-2px;right:-21px;line-height:18px}.alert-success{color:#468847;background-color:#dff0d8;border-color:#d6e9c6}.alert-danger,.alert-error{color:#b94a48;background-color:#f2dede;border-color:#eed3d7}.alert-info{color:#3a87ad;background-color:#d9edf7;border-color:#bce8f1}.alert-block{padding-top:14px;padding-bottom:14px}.alert-block>p,.alert-block>ul{margin-bottom:0}.alert-block p+p{margin-top:5px}.nav{margin-bottom:18px;margin-left:0;list-style:none}.nav>li>a{display:block}.nav>li>a:hover{text-decoration:none;background-color:#eee}.nav>.pull-right{float:right}.nav .nav-header{display:block;padding:3px 15px;font-size:11px;font-weight:bold;line-height:18px;color:#999;text-shadow:0 1px 0 rgba(255,255,255,0.5);text-transform:uppercase}.nav li+.nav-header{margin-top:9px}.nav-list{padding-right:15px;padding-left:15px;margin-bottom:0}.nav-list>li>a,.nav-list .nav-header{margin-right:-15px;margin-left:-15px;text-shadow:0 1px 0 rgba(255,255,255,0.5)}.nav-list>li>a{padding:3px 15px}.nav-list>.active>a,.nav-list>.active>a:hover{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.2);background-color:#08c}.nav-list [class^="icon-"]{margin-right:2px}.nav-list .divider{*width:100%;height:1px;margin:8px 1px;*margin:-5px 0 5px;overflow:hidden;background-color:#e5e5e5;border-bottom:1px solid #fff}.nav-tabs,.nav-pills{*zoom:1}.nav-tabs:before,.nav-pills:before,.nav-tabs:after,.nav-pills:after{display:table;content:""}.nav-tabs:after,.nav-pills:after{clear:both}.nav-tabs>li,.nav-pills>li{float:left}.nav-tabs>li>a,.nav-pills>li>a{padding-right:12px;padding-left:12px;margin-right:2px;line-height:14px}.nav-tabs{border-bottom:1px solid #ddd}.nav-tabs>li{margin-bottom:-1px}.nav-tabs>li>a{padding-top:8px;padding-bottom:8px;line-height:18px;border:1px solid transparent;-webkit-border-radius:4px 4px 0 0;-moz-border-radius:4px 4px 0 0;border-radius:4px 4px 0 0}.nav-tabs>li>a:hover{border-color:#eee #eee #ddd}.nav-tabs>.active>a,.nav-tabs>.active>a:hover{color:#555;cursor:default;background-color:#fff;border:1px solid #ddd;border-bottom-color:transparent}.nav-pills>li>a{padding-top:8px;padding-bottom:8px;margin-top:2px;margin-bottom:2px;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px}.nav-pills>.active>a,.nav-pills>.active>a:hover{color:#fff;background-color:#08c}.nav-stacked>li{float:none}.nav-stacked>li>a{margin-right:0}.nav-tabs.nav-stacked{border-bottom:0}.nav-tabs.nav-stacked>li>a{border:1px solid #ddd;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.nav-tabs.nav-stacked>li:first-child>a{-webkit-border-radius:4px 4px 0 0;-moz-border-radius:4px 4px 0 0;border-radius:4px 4px 0 0}.nav-tabs.nav-stacked>li:last-child>a{-webkit-border-radius:0 0 4px 4px;-moz-border-radius:0 0 4px 4px;border-radius:0 0 4px 4px}.nav-tabs.nav-stacked>li>a:hover{z-index:2;border-color:#ddd}.nav-pills.nav-stacked>li>a{margin-bottom:3px}.nav-pills.nav-stacked>li:last-child>a{margin-bottom:1px}.nav-tabs .dropdown-menu{-webkit-border-radius:0 0 5px 5px;-moz-border-radius:0 0 5px 5px;border-radius:0 0 5px 5px}.nav-pills .dropdown-menu{-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.nav-tabs .dropdown-toggle .caret,.nav-pills .dropdown-toggle .caret{margin-top:6px;border-top-color:#08c;border-bottom-color:#08c}.nav-tabs .dropdown-toggle:hover .caret,.nav-pills .dropdown-toggle:hover .caret{border-top-color:#005580;border-bottom-color:#005580}.nav-tabs .active .dropdown-toggle .caret,.nav-pills .active .dropdown-toggle .caret{border-top-color:#333;border-bottom-color:#333}.nav>.dropdown.active>a:hover{color:#000;cursor:pointer}.nav-tabs .open .dropdown-toggle,.nav-pills .open .dropdown-toggle,.nav>li.dropdown.open.active>a:hover{color:#fff;background-color:#999;border-color:#999}.nav li.dropdown.open .caret,.nav li.dropdown.open.active .caret,.nav li.dropdown.open a:hover .caret{border-top-color:#fff;border-bottom-color:#fff;opacity:1;filter:alpha(opacity=100)}.tabs-stacked .open>a:hover{border-color:#999}.tabbable{*zoom:1}.tabbable:before,.tabbable:after{display:table;content:""}.tabbable:after{clear:both}.tab-content{overflow:auto}.tabs-below>.nav-tabs,.tabs-right>.nav-tabs,.tabs-left>.nav-tabs{border-bottom:0}.tab-content>.tab-pane,.pill-content>.pill-pane{display:none}.tab-content>.active,.pill-content>.active{display:block}.tabs-below>.nav-tabs{border-top:1px solid #ddd}.tabs-below>.nav-tabs>li{margin-top:-1px;margin-bottom:0}.tabs-below>.nav-tabs>li>a{-webkit-border-radius:0 0 4px 4px;-moz-border-radius:0 0 4px 4px;border-radius:0 0 4px 4px}.tabs-below>.nav-tabs>li>a:hover{border-top-color:#ddd;border-bottom-color:transparent}.tabs-below>.nav-tabs>.active>a,.tabs-below>.nav-tabs>.active>a:hover{border-color:transparent #ddd #ddd #ddd}.tabs-left>.nav-tabs>li,.tabs-right>.nav-tabs>li{float:none}.tabs-left>.nav-tabs>li>a,.tabs-right>.nav-tabs>li>a{min-width:74px;margin-right:0;margin-bottom:3px}.tabs-left>.nav-tabs{float:left;margin-right:19px;border-right:1px solid #ddd}.tabs-left>.nav-tabs>li>a{margin-right:-1px;-webkit-border-radius:4px 0 0 4px;-moz-border-radius:4px 0 0 4px;border-radius:4px 0 0 4px}.tabs-left>.nav-tabs>li>a:hover{border-color:#eee #ddd #eee #eee}.tabs-left>.nav-tabs .active>a,.tabs-left>.nav-tabs .active>a:hover{border-color:#ddd transparent #ddd #ddd;*border-right-color:#fff}.tabs-right>.nav-tabs{float:right;margin-left:19px;border-left:1px solid #ddd}.tabs-right>.nav-tabs>li>a{margin-left:-1px;-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0}.tabs-right>.nav-tabs>li>a:hover{border-color:#eee #eee #eee #ddd}.tabs-right>.nav-tabs .active>a,.tabs-right>.nav-tabs .active>a:hover{border-color:#ddd #ddd #ddd transparent;*border-left-color:#fff}.navbar{*position:relative;*z-index:2;margin-bottom:18px;overflow:visible}.navbar-inner{min-height:40px;padding-right:20px;padding-left:20px;background-color:#2c2c2c;background-image:-moz-linear-gradient(top,#333,#222);background-image:-ms-linear-gradient(top,#333,#222);background-image:-webkit-gradient(linear,0 0,0 100%,from(#333),to(#222));background-image:-webkit-linear-gradient(top,#333,#222);background-image:-o-linear-gradient(top,#333,#222);background-image:linear-gradient(top,#333,#222);background-repeat:repeat-x;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;filter:progid:dximagetransform.microsoft.gradient(startColorstr='#333333',endColorstr='#222222',GradientType=0);-webkit-box-shadow:0 1px 3px rgba(0,0,0,0.25),inset 0 -1px 0 rgba(0,0,0,0.1);-moz-box-shadow:0 1px 3px rgba(0,0,0,0.25),inset 0 -1px 0 rgba(0,0,0,0.1);box-shadow:0 1px 3px rgba(0,0,0,0.25),inset 0 -1px 0 rgba(0,0,0,0.1)}.navbar .container{width:auto}.nav-collapse.collapse{height:auto}.navbar{color:#999}.navbar .brand:hover{text-decoration:none}.navbar .brand{display:block;float:left;padding:8px 20px 12px;margin-left:-20px;font-size:20px;font-weight:200;line-height:1;color:#999}.navbar .navbar-text{margin-bottom:0;line-height:40px}.navbar .navbar-link{color:#999}.navbar .navbar-link:hover{color:#fff}.navbar .btn,.navbar .btn-group{margin-top:5px}.navbar .btn-group .btn{margin:0}.navbar-form{margin-bottom:0;*zoom:1}.navbar-form:before,.navbar-form:after{display:table;content:""}.navbar-form:after{clear:both}.navbar-form input,.navbar-form select,.navbar-form .radio,.navbar-form .checkbox{margin-top:5px}.navbar-form input,.navbar-form select{display:inline-block;margin-bottom:0}.navbar-form input[type="image"],.navbar-form input[type="checkbox"],.navbar-form input[type="radio"]{margin-top:3px}.navbar-form .input-append,.navbar-form .input-prepend{margin-top:6px;white-space:nowrap}.navbar-form .input-append input,.navbar-form .input-prepend input{margin-top:0}.navbar-search{position:relative;float:left;margin-top:6px;margin-bottom:0}.navbar-search .search-query{padding:4px 9px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:13px;font-weight:normal;line-height:1;color:#fff;background-color:#626262;border:1px solid #151515;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,0.1),0 1px 0 rgba(255,255,255,0.15);-moz-box-shadow:inset 0 1px 2px rgba(0,0,0,0.1),0 1px 0 rgba(255,255,255,0.15);box-shadow:inset 0 1px 2px rgba(0,0,0,0.1),0 1px 0 rgba(255,255,255,0.15);-webkit-transition:none;-moz-transition:none;-ms-transition:none;-o-transition:none;transition:none}.navbar-search .search-query:-moz-placeholder{color:#ccc}.navbar-search .search-query:-ms-input-placeholder{color:#ccc}.navbar-search .search-query::-webkit-input-placeholder{color:#ccc}.navbar-search .search-query:focus,.navbar-search .search-query.focused{padding:5px 10px;color:#333;text-shadow:0 1px 0 #fff;background-color:#fff;border:0;outline:0;-webkit-box-shadow:0 0 3px rgba(0,0,0,0.15);-moz-box-shadow:0 0 3px rgba(0,0,0,0.15);box-shadow:0 0 3px rgba(0,0,0,0.15)}.navbar-fixed-top,.navbar-fixed-bottom{position:fixed;right:0;left:0;z-index:1030;margin-bottom:0}.navbar-fixed-top .navbar-inner,.navbar-fixed-bottom .navbar-inner{padding-right:0;padding-left:0;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:940px}.navbar-fixed-top{top:0}.navbar-fixed-bottom{bottom:0}.navbar .nav{position:relative;left:0;display:block;float:left;margin:0 10px 0 0}.navbar .nav.pull-right{float:right}.navbar .nav>li{display:block;float:left}.navbar .nav>li>a{float:none;padding:9px 10px 11px;line-height:19px;color:#999;text-decoration:none;text-shadow:0 -1px 0 rgba(0,0,0,0.25)}.navbar .btn{display:inline-block;padding:4px 10px 4px;margin:5px 5px 6px;line-height:18px}.navbar .btn-group{padding:5px 5px 6px;margin:0}.navbar .nav>li>a:hover{color:#fff;text-decoration:none;background-color:transparent}.navbar .nav .active>a,.navbar .nav .active>a:hover{color:#fff;text-decoration:none;background-color:#222}.navbar .divider-vertical{width:1px;height:40px;margin:0 9px;overflow:hidden;background-color:#222;border-right:1px solid #333}.navbar .nav.pull-right{margin-right:0;margin-left:10px}.navbar .btn-navbar{display:none;float:right;padding:7px 10px;margin-right:5px;margin-left:5px;background-color:#2c2c2c;*background-color:#222;background-image:-ms-linear-gradient(top,#333,#222);background-image:-webkit-gradient(linear,0 0,0 100%,from(#333),to(#222));background-image:-webkit-linear-gradient(top,#333,#222);background-image:-o-linear-gradient(top,#333,#222);background-image:linear-gradient(top,#333,#222);background-image:-moz-linear-gradient(top,#333,#222);background-repeat:repeat-x;border-color:#222 #222 #000;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:dximagetransform.microsoft.gradient(startColorstr='#333333',endColorstr='#222222',GradientType=0);filter:progid:dximagetransform.microsoft.gradient(enabled=false);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.075);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.075);box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.075)}.navbar .btn-navbar:hover,.navbar .btn-navbar:active,.navbar .btn-navbar.active,.navbar .btn-navbar.disabled,.navbar .btn-navbar[disabled]{background-color:#222;*background-color:#151515}.navbar .btn-navbar:active,.navbar .btn-navbar.active{background-color:#080808 \9}.navbar .btn-navbar .icon-bar{display:block;width:18px;height:2px;background-color:#f5f5f5;-webkit-border-radius:1px;-moz-border-radius:1px;border-radius:1px;-webkit-box-shadow:0 1px 0 rgba(0,0,0,0.25);-moz-box-shadow:0 1px 0 rgba(0,0,0,0.25);box-shadow:0 1px 0 rgba(0,0,0,0.25)}.btn-navbar .icon-bar+.icon-bar{margin-top:3px}.navbar .dropdown-menu:before{position:absolute;top:-7px;left:9px;display:inline-block;border-right:7px solid transparent;border-bottom:7px solid #ccc;border-left:7px solid transparent;border-bottom-color:rgba(0,0,0,0.2);content:''}.navbar .dropdown-menu:after{position:absolute;top:-6px;left:10px;display:inline-block;border-right:6px solid transparent;border-bottom:6px solid #fff;border-left:6px solid transparent;content:''}.navbar-fixed-bottom .dropdown-menu:before{top:auto;bottom:-7px;border-top:7px solid #ccc;border-bottom:0;border-top-color:rgba(0,0,0,0.2)}.navbar-fixed-bottom .dropdown-menu:after{top:auto;bottom:-6px;border-top:6px solid #fff;border-bottom:0}.navbar .nav li.dropdown .dropdown-toggle .caret,.navbar .nav li.dropdown.open .caret{border-top-color:#fff;border-bottom-color:#fff}.navbar .nav li.dropdown.active .caret{opacity:1;filter:alpha(opacity=100)}.navbar .nav li.dropdown.open>.dropdown-toggle,.navbar .nav li.dropdown.active>.dropdown-toggle,.navbar .nav li.dropdown.open.active>.dropdown-toggle{background-color:transparent}.navbar .nav li.dropdown.active>.dropdown-toggle:hover{color:#fff}.navbar .pull-right .dropdown-menu,.navbar .dropdown-menu.pull-right{right:0;left:auto}.navbar .pull-right .dropdown-menu:before,.navbar .dropdown-menu.pull-right:before{right:12px;left:auto}.navbar .pull-right .dropdown-menu:after,.navbar .dropdown-menu.pull-right:after{right:13px;left:auto}.breadcrumb{padding:7px 14px;margin:0 0 18px;list-style:none;background-color:#fbfbfb;background-image:-moz-linear-gradient(top,#fff,#f5f5f5);background-image:-ms-linear-gradient(top,#fff,#f5f5f5);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fff),to(#f5f5f5));background-image:-webkit-linear-gradient(top,#fff,#f5f5f5);background-image:-o-linear-gradient(top,#fff,#f5f5f5);background-image:linear-gradient(top,#fff,#f5f5f5);background-repeat:repeat-x;border:1px solid #ddd;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;filter:progid:dximagetransform.microsoft.gradient(startColorstr='#ffffff',endColorstr='#f5f5f5',GradientType=0);-webkit-box-shadow:inset 0 1px 0 #fff;-moz-box-shadow:inset 0 1px 0 #fff;box-shadow:inset 0 1px 0 #fff}.breadcrumb li{display:inline-block;*display:inline;text-shadow:0 1px 0 #fff;*zoom:1}.breadcrumb .divider{padding:0 5px;color:#999}.breadcrumb .active a{color:#333}.pagination{height:36px;margin:18px 0}.pagination ul{display:inline-block;*display:inline;margin-bottom:0;margin-left:0;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;*zoom:1;-webkit-box-shadow:0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:0 1px 2px rgba(0,0,0,0.05);box-shadow:0 1px 2px rgba(0,0,0,0.05)}.pagination li{display:inline}.pagination a{float:left;padding:0 14px;line-height:34px;text-decoration:none;border:1px solid #ddd;border-left-width:0}.pagination a:hover,.pagination .active a{background-color:#f5f5f5}.pagination .active a{color:#999;cursor:default}.pagination .disabled span,.pagination .disabled a,.pagination .disabled a:hover{color:#999;cursor:default;background-color:transparent}.pagination li:first-child a{border-left-width:1px;-webkit-border-radius:3px 0 0 3px;-moz-border-radius:3px 0 0 3px;border-radius:3px 0 0 3px}.pagination li:last-child a{-webkit-border-radius:0 3px 3px 0;-moz-border-radius:0 3px 3px 0;border-radius:0 3px 3px 0}.pagination-centered{text-align:center}.pagination-right{text-align:right}.pager{margin-bottom:18px;margin-left:0;text-align:center;list-style:none;*zoom:1}.pager:before,.pager:after{display:table;content:""}.pager:after{clear:both}.pager li{display:inline}.pager a{display:inline-block;padding:5px 14px;background-color:#fff;border:1px solid #ddd;-webkit-border-radius:15px;-moz-border-radius:15px;border-radius:15px}.pager a:hover{text-decoration:none;background-color:#f5f5f5}.pager .next a{float:right}.pager .previous a{float:left}.pager .disabled a,.pager .disabled a:hover{color:#999;cursor:default;background-color:#fff}.modal-open .dropdown-menu{z-index:2050}.modal-open .dropdown.open{*z-index:2050}.modal-open .popover{z-index:2060}.modal-open .tooltip{z-index:2070}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000}.modal-backdrop.fade{opacity:0}.modal-backdrop,.modal-backdrop.fade.in{opacity:.8;filter:alpha(opacity=80)}.modal{position:fixed;top:50%;left:50%;z-index:1050;width:560px;margin:-250px 0 0 -280px;overflow:auto;background-color:#fff;border:1px solid #999;border:1px solid rgba(0,0,0,0.3);*border:1px solid #999;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 3px 7px rgba(0,0,0,0.3);-moz-box-shadow:0 3px 7px rgba(0,0,0,0.3);box-shadow:0 3px 7px rgba(0,0,0,0.3);-webkit-background-clip:padding-box;-moz-background-clip:padding-box;background-clip:padding-box}.modal.fade{top:-25%;-webkit-transition:opacity .3s linear,top .3s ease-out;-moz-transition:opacity .3s linear,top .3s ease-out;-ms-transition:opacity .3s linear,top .3s ease-out;-o-transition:opacity .3s linear,top .3s ease-out;transition:opacity .3s linear,top .3s ease-out}.modal.fade.in{top:50%}.modal-header{padding:9px 15px;border-bottom:1px solid #eee}.modal-header .close{margin-top:2px}.modal-body{max-height:400px;padding:15px;overflow-y:auto}.modal-form{margin-bottom:0}.modal-footer{padding:14px 15px 15px;margin-bottom:0;text-align:right;background-color:#f5f5f5;border-top:1px solid #ddd;-webkit-border-radius:0 0 6px 6px;-moz-border-radius:0 0 6px 6px;border-radius:0 0 6px 6px;*zoom:1;-webkit-box-shadow:inset 0 1px 0 #fff;-moz-box-shadow:inset 0 1px 0 #fff;box-shadow:inset 0 1px 0 #fff}.modal-footer:before,.modal-footer:after{display:table;content:""}.modal-footer:after{clear:both}.modal-footer .btn+.btn{margin-bottom:0;margin-left:5px}.modal-footer .btn-group .btn+.btn{margin-left:-1px}.tooltip{position:absolute;z-index:1020;display:block;padding:5px;font-size:11px;opacity:0;filter:alpha(opacity=0);visibility:visible}.tooltip.in{opacity:.8;filter:alpha(opacity=80)}.tooltip.top{margin-top:-2px}.tooltip.right{margin-left:2px}.tooltip.bottom{margin-top:2px}.tooltip.left{margin-left:-2px}.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-top:5px solid #000;border-right:5px solid transparent;border-left:5px solid transparent}.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-top:5px solid transparent;border-bottom:5px solid transparent;border-left:5px solid #000}.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-right:5px solid transparent;border-bottom:5px solid #000;border-left:5px solid transparent}.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-top:5px solid transparent;border-right:5px solid #000;border-bottom:5px solid transparent}.tooltip-inner{max-width:200px;padding:3px 8px;color:#fff;text-align:center;text-decoration:none;background-color:#000;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.tooltip-arrow{position:absolute;width:0;height:0}.popover{position:absolute;top:0;left:0;z-index:1010;display:none;padding:5px}.popover.top{margin-top:-5px}.popover.right{margin-left:5px}.popover.bottom{margin-top:5px}.popover.left{margin-left:-5px}.popover.top .arrow{bottom:0;left:50%;margin-left:-5px;border-top:5px solid #000;border-right:5px solid transparent;border-left:5px solid transparent}.popover.right .arrow{top:50%;left:0;margin-top:-5px;border-top:5px solid transparent;border-right:5px solid #000;border-bottom:5px solid transparent}.popover.bottom .arrow{top:0;left:50%;margin-left:-5px;border-right:5px solid transparent;border-bottom:5px solid #000;border-left:5px solid transparent}.popover.left .arrow{top:50%;right:0;margin-top:-5px;border-top:5px solid transparent;border-bottom:5px solid transparent;border-left:5px solid #000}.popover .arrow{position:absolute;width:0;height:0}.popover-inner{width:280px;padding:3px;overflow:hidden;background:#000;background:rgba(0,0,0,0.8);-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 3px 7px rgba(0,0,0,0.3);-moz-box-shadow:0 3px 7px rgba(0,0,0,0.3);box-shadow:0 3px 7px rgba(0,0,0,0.3)}.popover-title{padding:9px 15px;line-height:1;background-color:#f5f5f5;border-bottom:1px solid #eee;-webkit-border-radius:3px 3px 0 0;-moz-border-radius:3px 3px 0 0;border-radius:3px 3px 0 0}.popover-content{padding:14px;background-color:#fff;-webkit-border-radius:0 0 3px 3px;-moz-border-radius:0 0 3px 3px;border-radius:0 0 3px 3px;-webkit-background-clip:padding-box;-moz-background-clip:padding-box;background-clip:padding-box}.popover-content p,.popover-content ul,.popover-content ol{margin-bottom:0}.thumbnails{margin-left:-20px;list-style:none;*zoom:1}.thumbnails:before,.thumbnails:after{display:table;content:""}.thumbnails:after{clear:both}.row-fluid .thumbnails{margin-left:0}.thumbnails>li{float:left;margin-bottom:18px;margin-left:20px}.thumbnail{display:block;padding:4px;line-height:1;border:1px solid #ddd;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:0 1px 1px rgba(0,0,0,0.075);-moz-box-shadow:0 1px 1px rgba(0,0,0,0.075);box-shadow:0 1px 1px rgba(0,0,0,0.075)}a.thumbnail:hover{border-color:#08c;-webkit-box-shadow:0 1px 4px rgba(0,105,214,0.25);-moz-box-shadow:0 1px 4px rgba(0,105,214,0.25);box-shadow:0 1px 4px rgba(0,105,214,0.25)}.thumbnail>img{display:block;max-width:100%;margin-right:auto;margin-left:auto}.thumbnail .caption{padding:9px}.label,.badge{font-size:10.998px;font-weight:bold;line-height:14px;color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);white-space:nowrap;vertical-align:baseline;background-color:#999}.label{padding:1px 4px 2px;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.badge{padding:1px 9px 2px;-webkit-border-radius:9px;-moz-border-radius:9px;border-radius:9px}a.label:hover,a.badge:hover{color:#fff;text-decoration:none;cursor:pointer}.label-important,.badge-important{background-color:#b94a48}.label-important[href],.badge-important[href]{background-color:#953b39}.label-warning,.badge-warning{background-color:#f89406}.label-warning[href],.badge-warning[href]{background-color:#c67605}.label-success,.badge-success{background-color:#468847}.label-success[href],.badge-success[href]{background-color:#356635}.label-info,.badge-info{background-color:#3a87ad}.label-info[href],.badge-info[href]{background-color:#2d6987}.label-inverse,.badge-inverse{background-color:#333}.label-inverse[href],.badge-inverse[href]{background-color:#1a1a1a}@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-moz-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-ms-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-o-keyframes progress-bar-stripes{from{background-position:0 0}to{background-position:40px 0}}@keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}.progress{height:18px;margin-bottom:18px;overflow:hidden;background-color:#f7f7f7;background-image:-moz-linear-gradient(top,#f5f5f5,#f9f9f9);background-image:-ms-linear-gradient(top,#f5f5f5,#f9f9f9);background-image:-webkit-gradient(linear,0 0,0 100%,from(#f5f5f5),to(#f9f9f9));background-image:-webkit-linear-gradient(top,#f5f5f5,#f9f9f9);background-image:-o-linear-gradient(top,#f5f5f5,#f9f9f9);background-image:linear-gradient(top,#f5f5f5,#f9f9f9);background-repeat:repeat-x;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;filter:progid:dximagetransform.microsoft.gradient(startColorstr='#f5f5f5',endColorstr='#f9f9f9',GradientType=0);-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,0.1);-moz-box-shadow:inset 0 1px 2px rgba(0,0,0,0.1);box-shadow:inset 0 1px 2px rgba(0,0,0,0.1)}.progress .bar{width:0;height:18px;font-size:12px;color:#fff;text-align:center;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#0e90d2;background-image:-moz-linear-gradient(top,#149bdf,#0480be);background-image:-webkit-gradient(linear,0 0,0 100%,from(#149bdf),to(#0480be));background-image:-webkit-linear-gradient(top,#149bdf,#0480be);background-image:-o-linear-gradient(top,#149bdf,#0480be);background-image:linear-gradient(top,#149bdf,#0480be);background-image:-ms-linear-gradient(top,#149bdf,#0480be);background-repeat:repeat-x;filter:progid:dximagetransform.microsoft.gradient(startColorstr='#149bdf',endColorstr='#0480be',GradientType=0);-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);-moz-box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);-webkit-box-sizing:border-box;-moz-box-sizing:border-box;-ms-box-sizing:border-box;box-sizing:border-box;-webkit-transition:width .6s ease;-moz-transition:width .6s ease;-ms-transition:width .6s ease;-o-transition:width .6s ease;transition:width .6s ease}.progress-striped .bar{background-color:#149bdf;background-image:-o-linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-webkit-linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-ms-linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);-webkit-background-size:40px 40px;-moz-background-size:40px 40px;-o-background-size:40px 40px;background-size:40px 40px}.progress.active .bar{-webkit-animation:progress-bar-stripes 2s linear infinite;-moz-animation:progress-bar-stripes 2s linear infinite;-ms-animation:progress-bar-stripes 2s linear infinite;-o-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}.progress-danger .bar{background-color:#dd514c;background-image:-moz-linear-gradient(top,#ee5f5b,#c43c35);background-image:-ms-linear-gradient(top,#ee5f5b,#c43c35);background-image:-webkit-gradient(linear,0 0,0 100%,from(#ee5f5b),to(#c43c35));background-image:-webkit-linear-gradient(top,#ee5f5b,#c43c35);background-image:-o-linear-gradient(top,#ee5f5b,#c43c35);background-image:linear-gradient(top,#ee5f5b,#c43c35);background-repeat:repeat-x;filter:progid:dximagetransform.microsoft.gradient(startColorstr='#ee5f5b',endColorstr='#c43c35',GradientType=0)}.progress-danger.progress-striped .bar{background-color:#ee5f5b;background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-ms-linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent)}.progress-success .bar{background-color:#5eb95e;background-image:-moz-linear-gradient(top,#62c462,#57a957);background-image:-ms-linear-gradient(top,#62c462,#57a957);background-image:-webkit-gradient(linear,0 0,0 100%,from(#62c462),to(#57a957));background-image:-webkit-linear-gradient(top,#62c462,#57a957);background-image:-o-linear-gradient(top,#62c462,#57a957);background-image:linear-gradient(top,#62c462,#57a957);background-repeat:repeat-x;filter:progid:dximagetransform.microsoft.gradient(startColorstr='#62c462',endColorstr='#57a957',GradientType=0)}.progress-success.progress-striped .bar{background-color:#62c462;background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-ms-linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent)}.progress-info .bar{background-color:#4bb1cf;background-image:-moz-linear-gradient(top,#5bc0de,#339bb9);background-image:-ms-linear-gradient(top,#5bc0de,#339bb9);background-image:-webkit-gradient(linear,0 0,0 100%,from(#5bc0de),to(#339bb9));background-image:-webkit-linear-gradient(top,#5bc0de,#339bb9);background-image:-o-linear-gradient(top,#5bc0de,#339bb9);background-image:linear-gradient(top,#5bc0de,#339bb9);background-repeat:repeat-x;filter:progid:dximagetransform.microsoft.gradient(startColorstr='#5bc0de',endColorstr='#339bb9',GradientType=0)}.progress-info.progress-striped .bar{background-color:#5bc0de;background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-ms-linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent)}.progress-warning .bar{background-color:#faa732;background-image:-moz-linear-gradient(top,#fbb450,#f89406);background-image:-ms-linear-gradient(top,#fbb450,#f89406);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fbb450),to(#f89406));background-image:-webkit-linear-gradient(top,#fbb450,#f89406);background-image:-o-linear-gradient(top,#fbb450,#f89406);background-image:linear-gradient(top,#fbb450,#f89406);background-repeat:repeat-x;filter:progid:dximagetransform.microsoft.gradient(startColorstr='#fbb450',endColorstr='#f89406',GradientType=0)}.progress-warning.progress-striped .bar{background-color:#fbb450;background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-ms-linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent)}.accordion{margin-bottom:18px}.accordion-group{margin-bottom:2px;border:1px solid #e5e5e5;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.accordion-heading{border-bottom:0}.accordion-heading .accordion-toggle{display:block;padding:8px 15px}.accordion-toggle{cursor:pointer}.accordion-inner{padding:9px 15px;border-top:1px solid #e5e5e5}.carousel{position:relative;margin-bottom:18px;line-height:1}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel .item{position:relative;display:none;-webkit-transition:.6s ease-in-out left;-moz-transition:.6s ease-in-out left;-ms-transition:.6s ease-in-out left;-o-transition:.6s ease-in-out left;transition:.6s ease-in-out left}.carousel .item>img{display:block;line-height:1}.carousel .active,.carousel .next,.carousel .prev{display:block}.carousel .active{left:0}.carousel .next,.carousel .prev{position:absolute;top:0;width:100%}.carousel .next{left:100%}.carousel .prev{left:-100%}.carousel .next.left,.carousel .prev.right{left:0}.carousel .active.left{left:-100%}.carousel .active.right{left:100%}.carousel-control{position:absolute;top:40%;left:15px;width:40px;height:40px;margin-top:-20px;font-size:60px;font-weight:100;line-height:30px;color:#fff;text-align:center;background:#222;border:3px solid #fff;-webkit-border-radius:23px;-moz-border-radius:23px;border-radius:23px;opacity:.5;filter:alpha(opacity=50)}.carousel-control.right{right:15px;left:auto}.carousel-control:hover{color:#fff;text-decoration:none;opacity:.9;filter:alpha(opacity=90)}.carousel-caption{position:absolute;right:0;bottom:0;left:0;padding:10px 15px 5px;background:#333;background:rgba(0,0,0,0.75)}.carousel-caption h4,.carousel-caption p{color:#fff}.hero-unit{padding:60px;margin-bottom:30px;background-color:#eee;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px}.hero-unit h1{margin-bottom:0;font-size:60px;line-height:1;letter-spacing:-1px;color:inherit}.hero-unit p{font-size:18px;font-weight:200;line-height:27px;color:inherit}.pull-right{float:right}.pull-left{float:left}.hide{display:none}.show{display:block}.invisible{visibility:hidden} 10 | --------------------------------------------------------------------------------