├── .gitignore ├── README.md ├── logos ├── 11x17poster.ai ├── 11x17poster.pdf ├── 11x17poster.png ├── calendar-button.png ├── calendar-button.sketch ├── calendar-button.svg ├── experimental.png ├── experimental.psd ├── favicon.png ├── favicon.psd ├── logo.png ├── logo.psd ├── logo_april.ai ├── logo_hires.png ├── logo_pride.ai ├── logo_pride.png ├── logo_vec.ai ├── logo_vec.svg ├── new_posterbase.psd ├── new_posterhass.psd ├── new_posterpick.psd ├── posterbase.psd ├── posterhass.psd └── posterpick.psd ├── new_scripts ├── CSB.xlsx ├── all_classes ├── classes_base.json ├── classes_extended.json ├── combiner.py ├── combiner2.py ├── combiner_ws.py ├── compile.sh ├── compiler.jar ├── course_six_renumbering.json ├── coursews.py ├── csb ├── csb.py ├── csb_raw ├── csb_to_full.sh ├── full.js ├── full.json ├── sublist ├── sublist.py ├── sublist2.py ├── sublist_ws.py ├── update_schedule.sh ├── ws └── xlsx2tsv.py ├── old_scripts ├── eval_scraper.py └── eval_to_json.py ├── update.py └── www ├── dataTables.scroller.min.js ├── full.js ├── img ├── Lab.gif ├── PartLab.gif ├── calendar-button.svg ├── cih1.gif ├── cihw.gif ├── cstimathon.png ├── fall.gif ├── fall_new.gif ├── fall_new.psd ├── grad.gif ├── hassA.gif ├── hassE.gif ├── hassH.gif ├── hassS.gif ├── iap.gif ├── js-large.jpeg ├── js-small.jpeg ├── logo.png ├── logo_pride.png ├── nonext.gif ├── projx.png ├── repeat.gif ├── rest.gif ├── spring.gif ├── spring_new.gif ├── spring_new.psd ├── summer.gif └── under.gif ├── index.html ├── jquery-ui.min.css ├── jquery-ui.min.js ├── jquery.timepicker.css ├── jquery.timepicker.min.js ├── jstest.html ├── old_www ├── ci-h.js ├── ci-hw.js ├── evaluations.html ├── evaluations.js ├── hass-a.js ├── hass-h.js ├── hass-s.js ├── logo.png ├── next-term.js ├── no-certs.html ├── professors.js ├── script.js └── stylesheet.css ├── privacy.html ├── script-compiled.js ├── script.js ├── scroller.dataTables.min.css ├── semesters ├── f17 │ ├── fall.html │ ├── fall17.js │ ├── script-compiled-fall.js │ └── script-fall.js ├── f18 │ ├── fall.html │ ├── fall.js │ └── script-compiled.js ├── f19 │ ├── fall.html │ ├── fall.js │ └── script-compiled.js ├── f20 │ ├── fall.html │ ├── fall.js │ └── script-compiled.js ├── f21 │ ├── fall.html │ ├── fall.js │ └── script-compiled.js ├── i18 │ ├── iap.html │ ├── iap.js │ ├── script-compiled-iap.js │ └── script-iap.js ├── i19 │ ├── iap.html │ ├── iap.js │ └── script-compiled.js ├── i22 │ ├── iap.html │ ├── iap.js │ └── script-compiled.js ├── s18 │ ├── script-compiled.js │ ├── script-spring.js │ ├── spring.html │ └── spring.js ├── s19 │ ├── script-compiled.js │ ├── spring.html │ └── spring.js ├── s20 │ ├── script-compiled.js │ ├── spring.html │ └── spring.js ├── s21 │ ├── script-compiled.js │ ├── spring.html │ └── spring.js └── s22 │ ├── script-compiled.js │ ├── spring.html │ └── spring.js └── stylesheet.css /.gitignore: -------------------------------------------------------------------------------- 1 | password 2 | chromedriver* 3 | geckodriver* 4 | evaluations 5 | evaluations_IAP 6 | old_scripts/evaluations.json 7 | old_scripts/professors.json 8 | *~ 9 | #* 10 | debug.log 11 | .DS_Store -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # firehose 2 | 3 | The source for [firehose.guide](https://firehose.guide). 4 | 5 | ## FAQ 6 | 7 | - Why does this code suck so much? 8 | 9 | The short answer is that I wrote it in a weekend when I was a freshman, and every new feature has been crammed on top of the existing JS. Does it work? It works pretty well, I guess? It could definitely be better. 10 | 11 | - Please bring Firehose to my school! 12 | 13 | I'm happy to help do this, although I'm not going to write new scrapers (as of July 2020), so there's some work to be done there. Still, if you're interested, I think it's definitely workable. 14 | 15 | 16 | ## License 17 | 18 | All rights reserved, for the time being. But if you're interested, please reach out. -------------------------------------------------------------------------------- /logos/11x17poster.ai: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/edfan/firehose/daaba0d1eef8a9d363cc39efc17e9df6ff96b610/logos/11x17poster.ai -------------------------------------------------------------------------------- /logos/11x17poster.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/edfan/firehose/daaba0d1eef8a9d363cc39efc17e9df6ff96b610/logos/11x17poster.pdf -------------------------------------------------------------------------------- /logos/11x17poster.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/edfan/firehose/daaba0d1eef8a9d363cc39efc17e9df6ff96b610/logos/11x17poster.png -------------------------------------------------------------------------------- /logos/calendar-button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/edfan/firehose/daaba0d1eef8a9d363cc39efc17e9df6ff96b610/logos/calendar-button.png -------------------------------------------------------------------------------- /logos/calendar-button.sketch: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/edfan/firehose/daaba0d1eef8a9d363cc39efc17e9df6ff96b610/logos/calendar-button.sketch -------------------------------------------------------------------------------- /logos/calendar-button.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | btn_google_signin_light_normal_web 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | Export to Google Calendar 33 | 34 | 35 | -------------------------------------------------------------------------------- /logos/experimental.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/edfan/firehose/daaba0d1eef8a9d363cc39efc17e9df6ff96b610/logos/experimental.png -------------------------------------------------------------------------------- /logos/experimental.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/edfan/firehose/daaba0d1eef8a9d363cc39efc17e9df6ff96b610/logos/experimental.psd -------------------------------------------------------------------------------- /logos/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/edfan/firehose/daaba0d1eef8a9d363cc39efc17e9df6ff96b610/logos/favicon.png -------------------------------------------------------------------------------- /logos/favicon.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/edfan/firehose/daaba0d1eef8a9d363cc39efc17e9df6ff96b610/logos/favicon.psd -------------------------------------------------------------------------------- /logos/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/edfan/firehose/daaba0d1eef8a9d363cc39efc17e9df6ff96b610/logos/logo.png -------------------------------------------------------------------------------- /logos/logo.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/edfan/firehose/daaba0d1eef8a9d363cc39efc17e9df6ff96b610/logos/logo.psd -------------------------------------------------------------------------------- /logos/logo_april.ai: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/edfan/firehose/daaba0d1eef8a9d363cc39efc17e9df6ff96b610/logos/logo_april.ai -------------------------------------------------------------------------------- /logos/logo_hires.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/edfan/firehose/daaba0d1eef8a9d363cc39efc17e9df6ff96b610/logos/logo_hires.png -------------------------------------------------------------------------------- /logos/logo_pride.ai: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/edfan/firehose/daaba0d1eef8a9d363cc39efc17e9df6ff96b610/logos/logo_pride.ai -------------------------------------------------------------------------------- /logos/logo_pride.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/edfan/firehose/daaba0d1eef8a9d363cc39efc17e9df6ff96b610/logos/logo_pride.png -------------------------------------------------------------------------------- /logos/logo_vec.ai: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/edfan/firehose/daaba0d1eef8a9d363cc39efc17e9df6ff96b610/logos/logo_vec.ai -------------------------------------------------------------------------------- /logos/new_posterbase.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/edfan/firehose/daaba0d1eef8a9d363cc39efc17e9df6ff96b610/logos/new_posterbase.psd -------------------------------------------------------------------------------- /logos/new_posterhass.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/edfan/firehose/daaba0d1eef8a9d363cc39efc17e9df6ff96b610/logos/new_posterhass.psd -------------------------------------------------------------------------------- /logos/new_posterpick.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/edfan/firehose/daaba0d1eef8a9d363cc39efc17e9df6ff96b610/logos/new_posterpick.psd -------------------------------------------------------------------------------- /logos/posterbase.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/edfan/firehose/daaba0d1eef8a9d363cc39efc17e9df6ff96b610/logos/posterbase.psd -------------------------------------------------------------------------------- /logos/posterhass.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/edfan/firehose/daaba0d1eef8a9d363cc39efc17e9df6ff96b610/logos/posterhass.psd -------------------------------------------------------------------------------- /logos/posterpick.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/edfan/firehose/daaba0d1eef8a9d363cc39efc17e9df6ff96b610/logos/posterpick.psd -------------------------------------------------------------------------------- /new_scripts/CSB.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/edfan/firehose/daaba0d1eef8a9d363cc39efc17e9df6ff96b610/new_scripts/CSB.xlsx -------------------------------------------------------------------------------- /new_scripts/combiner.py: -------------------------------------------------------------------------------- 1 | import json 2 | import copy 3 | 4 | with open('csb') as f: 5 | times = json.load(f) 6 | 7 | with open('sublist') as f: 8 | descs = json.load(f) 9 | 10 | with open('evaluations') as f: 11 | evals = json.load(f) 12 | 13 | classes = {} 14 | 15 | for c in times: 16 | classes[c] = { 17 | 'no': c, 18 | 'co': times[c]['course'], 19 | 'cl': times[c]['class'], 20 | 'f': times[c]['final'], 21 | 'tb': times[c]['tba'], 22 | 23 | 's': times[c]['sections'], 24 | 'l': times[c]['l'], 25 | 'r': times[c]['r'], 26 | 'b': times[c]['b'], 27 | 'lr': times[c]['l_raw'], 28 | 'rr': times[c]['r_raw'], 29 | 'br': times[c]['b_raw']} 30 | 31 | if c in descs: 32 | classes[c]['hh'] = descs[c]['HASS-H'] 33 | classes[c]['ha'] = descs[c]['HASS-A'] 34 | classes[c]['hs'] = descs[c]['HASS-S'] 35 | classes[c]['he'] = descs[c]['HASS-E'] 36 | classes[c]['ci'] = descs[c]['CI-H'] 37 | classes[c]['cw'] = descs[c]['CI-HW'] 38 | classes[c]['nx'] = descs[c]['no_next'] 39 | classes[c]['rp'] = descs[c]['repeat'] 40 | classes[c]['re'] = descs[c]['REST'] 41 | classes[c]['la'] = descs[c]['LAB'] 42 | classes[c]['pl'] = descs[c]['pLAB'] 43 | classes[c]['u1'] = descs[c]['units1'] 44 | classes[c]['u2'] = descs[c]['units2'] 45 | classes[c]['u3'] = descs[c]['units3'] 46 | classes[c]['le'] = descs[c]['level'] 47 | classes[c]['sa'] = descs[c]['same_as'] 48 | classes[c]['mw'] = descs[c]['meets_with'] 49 | classes[c]['u'] = descs[c]['url'] 50 | classes[c]['t'] = descs[c]['terms'] 51 | classes[c]['pr'] = descs[c]['prereq'] 52 | classes[c]['d'] = descs[c]['desc'] 53 | classes[c]['n'] = descs[c]['name'] 54 | else: 55 | classes[c]['hh'] = False 56 | classes[c]['ha'] = False 57 | classes[c]['hs'] = False 58 | classes[c]['he'] = False 59 | classes[c]['ci'] = False 60 | classes[c]['cw'] = False 61 | classes[c]['rp'] = False 62 | classes[c]['re'] = False 63 | classes[c]['la'] = False 64 | classes[c]['pl'] = False 65 | classes[c]['u1'] = 0 66 | classes[c]['u2'] = 0 67 | classes[c]['u3'] = 0 68 | classes[c]['sa'] = '' 69 | classes[c]['mw'] = '' 70 | classes[c]['le'] = 'U' 71 | classes[c]['t'] = ['FA'] 72 | classes[c]['pr'] = 'None' 73 | classes[c]['d'] = "This class is in the registrar's schedule, but not the course catalog." 74 | classes[c]['n'] = 'Special Subject' 75 | 76 | if c in evals: 77 | total_rating = 0 78 | total_hours = 0 79 | total_size = 0 80 | terms = 0 81 | 82 | for t in evals[c]: 83 | if t['resp'] > 0: 84 | total_rating += t['rating'] 85 | total_hours += t['oc_hours'] + t['ic_hours'] 86 | total_size += t['eligible'] 87 | terms += 1 88 | 89 | if terms == 0: 90 | terms = 1 91 | 92 | classes[c]['ra'] = round(total_rating / terms, 1) 93 | classes[c]['h'] = round(total_hours / terms, 1) 94 | classes[c]['si'] = round(total_size / terms, 1) 95 | else: 96 | classes[c]['ra'] = 0 97 | classes[c]['h'] = 0 98 | classes[c]['si'] = 0 99 | 100 | try: 101 | # Special case 14.01/14.02 rec-only sections. 102 | classes['14.01R'] = copy.deepcopy(classes['14.01']) 103 | classes['14.01']['r'] = classes['14.01']['r'][:2] 104 | classes['14.01']['rr'] = classes['14.01']['rr'][:2] 105 | classes['14.01R']['no'] = '14.01R' 106 | classes['14.01R']['s'] = ['r'] 107 | classes['14.01R']['r'] = classes['14.01R']['r'][2:] 108 | classes['14.01R']['rr'] = classes['14.01R']['rr'][2:] 109 | classes['14.01R']['n'] += " (recitation only)" 110 | del classes['14.01R']['l'] 111 | 112 | 113 | classes['14.02R'] = copy.deepcopy(classes['14.02']) 114 | classes['14.02']['r'] = classes['14.02']['r'][:2] 115 | classes['14.02']['rr'] = classes['14.02']['rr'][:2] 116 | classes['14.02R']['no'] = '14.02R' 117 | classes['14.02R']['s'] = ['r'] 118 | classes['14.02R']['r'] = classes['14.02R']['r'][2:] 119 | classes['14.02R']['rr'] = classes['14.02R']['rr'][2:] 120 | classes['14.02R']['n'] += " (recitation only)" 121 | del classes['14.02R']['l'] 122 | 123 | 124 | except Exception as e: 125 | print(e) 126 | 127 | with open('full.json', 'w') as f: 128 | f.write('var classes = ') 129 | json.dump(classes, f, separators=(',', ':')) 130 | f.write(';') 131 | 132 | 133 | 134 | 135 | -------------------------------------------------------------------------------- /new_scripts/combiner2.py: -------------------------------------------------------------------------------- 1 | import json 2 | import copy 3 | from functools import cmp_to_key 4 | 5 | def class_sort_internal(a, b): 6 | if len(a) < len(b): 7 | return -1 8 | elif len(a) > len(b): 9 | return 1 10 | 11 | for i in range(len(a)): 12 | if a[i] < b[i]: 13 | return -1 14 | elif a[i] > b[i]: 15 | return 1 16 | 17 | return 0 18 | 19 | def class_sort_internal2(a, b): 20 | if len(a) < len(b): 21 | mult = -1 22 | length = len(a) 23 | elif len(a) > len(b): 24 | mult = 1 25 | length = len(b) 26 | else: 27 | mult = 0 28 | length = len(a) 29 | 30 | for i in range(length): 31 | if a[i] < b[i]: 32 | return -1 33 | elif a[i] > b[i]: 34 | return 1 35 | 36 | return mult 37 | 38 | def class_sort(a, b): 39 | a_s = a['u'].split('.') 40 | b_s = b['u'].split('.') 41 | 42 | sort = class_sort_internal(a_s[0], b_s[0]) 43 | if sort == 0: 44 | sort = class_sort_internal2(a_s[1], b_s[1]) 45 | 46 | return sort 47 | 48 | with open('csb') as f: 49 | times = json.load(f) 50 | 51 | with open('sublist') as f: 52 | descs = json.load(f) 53 | 54 | with open('evaluations') as f: 55 | evals = json.load(f) 56 | 57 | classes_base = [] 58 | classes_extended = [] 59 | 60 | """ 61 | One-character mappings: 62 | 63 | Base: 64 | c: sections (dict(str->sec_array)) 65 | h: hours (decimal) 66 | n: name (str) 67 | r: rating (decimal) 68 | s: sort (int) 69 | u: number (str) 70 | 71 | Extended: 72 | a: same-as (str) 73 | d: description (str) 74 | f: flags (dict) 75 | l: level ('U' || 'G') 76 | m: meets-with (str) 77 | n: units (int[3]) 78 | p: prereqs (str) 79 | r: url (str) 80 | t: terms (list) 81 | u: number (str) 82 | w: raw sections (dict(str->sec_array)) 83 | z: size (decimal) 84 | 85 | Flags: 86 | a: HASS-A 87 | c: CI-H 88 | e: HASS-E 89 | f: final 90 | h: HASS-H 91 | l: Institute Lab 92 | n: not offered next year 93 | p: Partial Lab 94 | r: REST 95 | s: HASS-S 96 | t: TBA section 97 | u: repeat for credit 98 | w: CI-HW 99 | """ 100 | 101 | for c in times: 102 | cl = { 103 | 'c': {}, 104 | 'u': c, 105 | } 106 | 107 | cl_e = { 108 | 'u': c, 109 | 'f': {}, 110 | 'w': {} 111 | } 112 | 113 | for s in times[c]['sections']: 114 | cl['c'][s] = times[c][s] 115 | cl_e['w'][s] = times[c][s + '_raw'] 116 | 117 | if times[c]['final']: 118 | cl_e['f']['f'] = 0 119 | if times[c]['tba']: 120 | cl_e['f']['t'] = 0 121 | 122 | if c in descs: 123 | cl['n'] = descs[c]['name'] 124 | cl_e['d'] = descs[c]['desc'] 125 | 126 | if descs[c]['HASS-H']: 127 | cl_e['f']['h'] = 0 128 | if descs[c]['HASS-A']: 129 | cl_e['f']['a'] = 0 130 | if descs[c]['HASS-S']: 131 | cl_e['f']['s'] = 0 132 | if descs[c]['HASS-E']: 133 | cl_e['f']['e'] = 0 134 | if descs[c]['CI-H']: 135 | cl_e['f']['c'] = 0 136 | if descs[c]['CI-HW']: 137 | cl_e['f']['w'] = 0 138 | if descs[c]['no_next']: 139 | cl_e['f']['n'] = 0 140 | if descs[c]['repeat']: 141 | cl_e['f']['u'] = 0 142 | if descs[c]['REST']: 143 | cl_e['f']['r'] = 0 144 | if descs[c]['LAB']: 145 | cl_e['f']['l'] = 0 146 | if descs[c]['pLAB']: 147 | cl_e['f']['p'] = 0 148 | 149 | cl_e['n'] = [descs[c]['units1'], descs[c]['units2'], descs[c]['units3']] 150 | cl_e['l'] = descs[c]['level'] 151 | cl_e['t'] = descs[c]['terms'] 152 | cl_e['r'] = descs[c]['url'] 153 | cl_e['m'] = descs[c]['meets_with'] 154 | cl_e['a'] = descs[c]['same_as'] 155 | cl_e['p'] = descs[c]['prereq'] 156 | else: 157 | cl['n'] = 'Special Subject' 158 | cl['d'] = "This class is in the registrar's schedule, but not the course catalog." 159 | 160 | cl_e['n'] = [0, 0, 0] 161 | cl_e['l'] = 'U' 162 | cl_e['t'] = ['FA'] 163 | cl_e['r'] = '' 164 | cl_e['m'] = '' 165 | cl_e['a'] = '' 166 | cl_e['p'] = '' 167 | 168 | if c in evals: 169 | total_rating = 0 170 | total_hours = 0 171 | total_size = 0 172 | terms = 0 173 | 174 | for t in evals[c]: 175 | if t['resp'] > 0: 176 | total_rating += t['rating'] 177 | total_hours += t['oc_hours'] + t['ic_hours'] 178 | total_size += t['eligible'] 179 | terms += 1 180 | 181 | if terms == 0: 182 | terms = 1 183 | 184 | cl['r'] = round(total_rating / terms, 1) 185 | cl['h'] = round(total_hours / terms, 1) 186 | cl_e['z'] = round(total_size / terms, 1) 187 | else: 188 | cl['r'] = 0 189 | cl['h'] = 0 190 | cl_e['z'] = 0 191 | 192 | classes_base.append(cl) 193 | classes_extended.append(cl_e) 194 | 195 | # Special case 14.01/14.02 rec-only sections. 196 | index = next(index for (index, d) in enumerate(classes_base) if d['u'] == '14.01') 197 | temp_base = copy.deepcopy(classes_base[index]) 198 | temp_extended = copy.deepcopy(classes_extended[index]) 199 | classes_base[index]['c']['r'] = temp_base['c']['r'][:5] 200 | temp_base['u'] = '14.01R' 201 | temp_extended['u'] = '14.01R' 202 | temp_base['c'] = { 'r': temp_base['c']['r'][5:] } 203 | temp_base['n'] += " (recitation only)" 204 | classes_base.insert(index + 1, temp_base) 205 | classes_extended.insert(index + 1, temp_extended) 206 | 207 | index = next(index for (index, d) in enumerate(classes_base) if d['u'] == '14.02') 208 | temp_base = copy.deepcopy(classes_base[index]) 209 | temp_extended = copy.deepcopy(classes_extended[index]) 210 | classes_base[index]['c']['r'] = temp_base['c']['r'][:2] 211 | temp_base['u'] = '14.01R' 212 | temp_extended['u'] = '14.01R' 213 | temp_base['c'] = { 'r': temp_base['c']['r'][2:] } 214 | temp_base['n'] += " (recitation only)" 215 | classes_base.insert(index + 1, temp_base) 216 | classes_extended.insert(index + 1, temp_extended) 217 | 218 | # Calculate a sorting order. 219 | classes_base.sort(key=cmp_to_key(class_sort)) 220 | for i in range(len(classes_base)): 221 | classes_base[i]['s'] = i 222 | 223 | # Convert to dict. 224 | classes_base_d = {} 225 | classes_extended_d = {} 226 | 227 | for c in classes_base: 228 | classes_base_d[c['u']] = c 229 | for c in classes_extended: 230 | classes_extended_d[c['u']] = c 231 | 232 | with open('classes_base.json', 'w') as f: 233 | f.write('var classes = ') 234 | json.dump(classes_base_d, f, separators=(',', ':')) 235 | f.write(';') 236 | 237 | with open('classes_extended.json', 'w') as f: 238 | f.write('var classes_ex = ') 239 | json.dump(classes_extended_d, f, separators=(',', ':')) 240 | f.write(';') 241 | -------------------------------------------------------------------------------- /new_scripts/combiner_ws.py: -------------------------------------------------------------------------------- 1 | import json 2 | import copy 3 | import datetime 4 | 5 | with open('ws') as f: 6 | ws = json.load(f) 7 | 8 | with open('sublist') as f: 9 | sublist = json.load(f) 10 | 11 | with open('evaluations') as f: 12 | evals = json.load(f) 13 | 14 | # Special case 6.871 evals. 15 | # evals['6.871'] = evals['HST.956'] 16 | 17 | classes = {} 18 | 19 | def all_virtual(sections): 20 | for s in sections: 21 | if s[1] != 'Virtual': 22 | return False 23 | return True 24 | 25 | for c in ws: 26 | classes[c] = { 27 | 'no': c, 28 | 'co': ws[c]['course'], 29 | 'cl': ws[c]['class'], 30 | 'tb': ws[c]['tba'], 31 | 32 | 's': ws[c]['sections'], 33 | 'l': ws[c]['l'], 34 | 'r': ws[c]['r'], 35 | 'b': ws[c]['b'], 36 | 'lr': ws[c]['l_raw'], 37 | 'rr': ws[c]['r_raw'], 38 | 'br': ws[c]['b_raw']} 39 | 40 | classes[c]['hh'] = ws[c]['HASS-H'] 41 | classes[c]['ha'] = ws[c]['HASS-A'] 42 | classes[c]['hs'] = ws[c]['HASS-S'] 43 | classes[c]['he'] = ws[c]['HASS-E'] 44 | classes[c]['ci'] = ws[c]['CI-H'] 45 | classes[c]['cw'] = ws[c]['CI-HW'] 46 | classes[c]['re'] = ws[c]['REST'] 47 | classes[c]['la'] = ws[c]['LAB'] 48 | classes[c]['pl'] = ws[c]['pLAB'] 49 | classes[c]['u1'] = ws[c]['units1'] 50 | classes[c]['u2'] = ws[c]['units2'] 51 | classes[c]['u3'] = ws[c]['units3'] 52 | classes[c]['le'] = ws[c]['level'] 53 | classes[c]['sa'] = ws[c]['same_as'] 54 | classes[c]['mw'] = ws[c]['meets_with'] 55 | classes[c]['lm'] = ws[c]['limited'] 56 | classes[c]['t'] = ws[c]['terms'] 57 | classes[c]['pr'] = ws[c]['prereq'] 58 | classes[c]['d'] = ws[c]['desc'] 59 | classes[c]['n'] = ws[c]['name'] 60 | 61 | classes[c]['i'] = ws[c]['in-charge'] 62 | classes[c]['v'] = all_virtual(ws[c]['l'] + ws[c]['r'] + ws[c]['b']) 63 | 64 | 65 | if c in sublist: 66 | classes[c]['nx'] = sublist[c]['no_next'] 67 | classes[c]['hf'] = sublist[c]['half'] 68 | classes[c]['rp'] = sublist[c]['repeat'] 69 | classes[c]['u'] = sublist[c]['url'] 70 | try: 71 | classes[c]['f'] = sublist[c]['final'] 72 | except: 73 | print('failed to get final for', c) 74 | classes[c]['f'] = False 75 | if 'old_num' in sublist[c]: 76 | classes[c]['on'] = sublist[c]['old_num'] 77 | classes[c]['n'] = "[" + sublist[c]['old_num'] + "] " + classes[c]['n'] 78 | else: 79 | classes[c]['nx'] = False 80 | classes[c]['hf'] = False 81 | classes[c]['rp'] = False 82 | classes[c]['u'] = '' 83 | classes[c]['f'] = False 84 | 85 | old_c = classes[c].get('on', None) 86 | if c in evals or (old_c and old_c in evals): 87 | total_rating = 0 88 | total_hours = 0 89 | total_size = 0 90 | terms = 0 91 | 92 | evals_ = evals[old_c] if old_c else evals[c] 93 | for t in evals_: 94 | if t['resp'] > 0: 95 | total_rating += t['rating'] 96 | total_hours += t['oc_hours'] + t['ic_hours'] 97 | total_size += t['eligible'] 98 | terms += 1 99 | 100 | if terms == 0: 101 | terms = 1 102 | 103 | classes[c]['ra'] = round(total_rating / terms, 1) 104 | classes[c]['h'] = round(total_hours / terms, 1) 105 | classes[c]['si'] = round(total_size / terms, 1) 106 | else: 107 | classes[c]['ra'] = 0 108 | classes[c]['h'] = 0 109 | classes[c]['si'] = 0 110 | 111 | # Special case 2.008 schedule. 112 | # classes['2.008']['s'] = ['l', 'b'] 113 | # classes['2.008']['r'] = [] 114 | 115 | # Special case 6.S977. 116 | classes['6.S977']['u1'] = 3 117 | classes['6.S977']['u2'] = 0 118 | classes['6.S977']['u3'] = 9 119 | classes['6.S977']['n'] = 'The Sum of Squares Methods' 120 | classes['6.S977']['d'] = 'Study of algorithms and computational complexity through the lens of the Sum of Squares method (SoS), a powerful approach to algorithm design generalizing linear programming and spectral methods. Specific sub-topics vary and are chosen with student input, potentially including algorithms for combinatorial and continuous optimization (graphs, constraint satisfaction problems, unique games conjecture), applications to high-dimensional algorithmic statistics (robustness, privacy, method of moments), applications to quantum information, and an SoS perspective on computational complexity (of NP-hard problems and/or of statistical inference).' 121 | 122 | classes['21M.707']['d'] = 'Explore performance and cultural production of Black intellectuals and artists on Broadway and in local communities. Engage with intersections of race, class, gender, sexuality, abilities, and social justice. Class discussions, diverse readings, creative assignments, live performances, exhibits, and guest artists enrich our study. Everyone is welcome. No prerequisites.' 123 | 124 | """ try: 125 | # Special case 14.01/14.02 rec-only sections. 126 | classes['14.01R'] = copy.deepcopy(classes['14.01']) 127 | classes['14.01']['r'] = classes['14.01']['r'][:2] 128 | classes['14.01']['rr'] = classes['14.01']['rr'][:2] 129 | classes['14.01R']['no'] = '14.01R' 130 | classes['14.01R']['s'] = ['r'] 131 | classes['14.01R']['r'] = classes['14.01R']['r'][2:] 132 | classes['14.01R']['rr'] = classes['14.01R']['rr'][2:] 133 | classes['14.01R']['n'] += " (recitation only)" 134 | del classes['14.01R']['l'] 135 | 136 | 137 | classes['14.02R'] = copy.deepcopy(classes['14.02']) 138 | classes['14.02']['r'] = classes['14.02']['r'][:2] 139 | classes['14.02']['rr'] = classes['14.02']['rr'][:2] 140 | classes['14.02R']['no'] = '14.02R' 141 | classes['14.02R']['s'] = ['r'] 142 | classes['14.02R']['r'] = classes['14.02R']['r'][2:] 143 | classes['14.02R']['rr'] = classes['14.02R']['rr'][2:] 144 | classes['14.02R']['n'] += " (recitation only)" 145 | del classes['14.02R']['l'] 146 | 147 | 148 | except Exception as e: 149 | print(e) """ 150 | 151 | classes['22.05']['l'] = [[[[33,3],[93,3]],"24-121"]] 152 | classes['22.05']['r'] = [[[[124,2]],"24-121"]] 153 | 154 | last_update = datetime.datetime.now().strftime('%Y-%m-%d %l:%M %p') 155 | 156 | with open('full.js', 'w') as f: 157 | f.write('var last_update = "' + last_update + '";\n') 158 | f.write('var classes = ') 159 | json.dump(classes, f, separators=(',', ':')) 160 | f.write(';') 161 | 162 | with open('full.json', 'w') as f: 163 | obj = {} 164 | obj["lastUpdated"] = last_update 165 | obj["classes"] = classes 166 | json.dump(obj, f, separators=(',', ':')) 167 | -------------------------------------------------------------------------------- /new_scripts/compile.sh: -------------------------------------------------------------------------------- 1 | java -jar compiler.jar --js ../www/script.js --js_output_file ../www/script-compiled.js -------------------------------------------------------------------------------- /new_scripts/compiler.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/edfan/firehose/daaba0d1eef8a9d363cc39efc17e9df6ff96b610/new_scripts/compiler.jar -------------------------------------------------------------------------------- /new_scripts/course_six_renumbering.json: -------------------------------------------------------------------------------- 1 | { 2 | "6.0001": "6.100A", 3 | "6.0002": "6.100B", 4 | "6.0004": "6.1900", 5 | "6.000L": "6.100L", 6 | "6.002": "6.2000", 7 | "6.003": "6.3000", 8 | "6.004": "6.1910", 9 | "6.005": "6.3100", 10 | "6.006": "6.1210", 11 | "6.008": "6.3800", 12 | "6.009": "6.1010", 13 | "6.01": "6.9080", 14 | "6.010": "6.9000", 15 | "6.011": "6.3010", 16 | "6.012": "6.2500", 17 | "6.013": "6.2300", 18 | "6.014": "6.2210", 19 | "6.015": "6.2540", 20 | "6.02": "6.3400", 21 | "6.020": "6.4800", 22 | "6.021": "6.4810", 23 | "6.022": "6.4820", 24 | "6.023": "6.4830", 25 | "6.024": "6.4840", 26 | "6.025": "6.4860", 27 | "6.026": "6.8801", 28 | "6.027": "6.4850", 29 | "6.03": "6.4900", 30 | "6.031": "6.1020", 31 | "6.033": "6.1800", 32 | "6.034": "6.4100", 33 | "6.035": "6.1100", 34 | "6.036": "6.3900", 35 | "6.037": "6.9550", 36 | "6.038": "6.4110", 37 | "6.039": "6.1810", 38 | "6.041": "6.3700", 39 | "6.042": "6.1200", 40 | "6.042A": "6.120A", 41 | "6.043": "6.3800", 42 | "6.045": "6.1400", 43 | "6.046": "6.1220", 44 | "6.047": "6.8701", 45 | "6.049": "6.4710", 46 | "6.050": "6.2490", 47 | "6.051": "6.2400", 48 | "6.052": "6.1850", 49 | "6.053": "6.1600", 50 | "6.054": "6.1420", 51 | "6.057": "6.9500", 52 | "6.058": "6.9510", 53 | "6.061": "6.2200", 54 | "6.070": "6.2020", 55 | "6.073": "6.4570", 56 | "6.08": "6.9010", 57 | "6.100": "6.9801", 58 | "6.101": "6.2040", 59 | "6.111": "6.2050", 60 | "6.115": "6.2060", 61 | "6.1151": "6.2061", 62 | "6.117": "6.9520", 63 | "6.123": "6.4870", 64 | "6.129": "6.4880", 65 | "6.131": "6.2220", 66 | "6.1311": "6.2221", 67 | "6.141": "6.4200", 68 | "6.146": "6.9600", 69 | "6.147": "6.9610", 70 | "6.148": "6.9620", 71 | "6.150": "6.9640", 72 | "6.151": "6.9650", 73 | "6.152": "6.2600", 74 | "6.157": "6.2410", 75 | "6.161": "6.2370", 76 | "6.163": "6.9030", 77 | "6.170": "6.1040", 78 | "6.172": "6.1060", 79 | "6.175": "6.1920", 80 | "6.176": "6.9630", 81 | "6.178": "6.9560", 82 | "6.179": "6.9570", 83 | "6.182": "6.4890", 84 | "6.185": "6.4550", 85 | "6.187": "6.3020", 86 | "6.207": "6.3260", 87 | "6.215": "6.7201", 88 | "6.231": "6.7940", 89 | "6.241": "6.7100", 90 | "6.244": "6.7820", 91 | "6.245": "6.7110", 92 | "6.246": "6.7950", 93 | "6.247": "6.7120", 94 | "6.248": "6.7380", 95 | "6.249": "6.7390", 96 | "6.251": "6.7210", 97 | "6.252": "6.7220", 98 | "6.254": "6.7240", 99 | "6.255": "6.7200", 100 | "6.256": "6.7230", 101 | "6.260": "6.7480", 102 | "6.261": "6.7490", 103 | "6.262": "6.7710", 104 | "6.263": "6.7450", 105 | "6.265": "6.7720", 106 | "6.267": "6.7420", 107 | "6.268": "6.7260", 108 | "6.300": "6.2080", 109 | "6.301": "6.2090", 110 | "6.302": "6.3100", 111 | "6.320": "6.3102", 112 | "6.321": "6.2092", 113 | "6.330": "6.2222", 114 | "6.332": "6.6270", 115 | "6.333": "6.6030", 116 | "6.334": "6.6220", 117 | "6.335": "6.7340", 118 | "6.336": "6.7300", 119 | "6.337": "6.7310", 120 | "6.338": "6.7320", 121 | "6.339": "6.7330", 122 | "6.341": "6.7000", 123 | "6.344": "6.7010", 124 | "6.345": "6.8620", 125 | "6.347": "6.7050", 126 | "6.348": "6.7060", 127 | "6.374": "6.6010", 128 | "6.375": "6.5910", 129 | "6.401": "6.3720", 130 | "6.404": "6.3950", 131 | "6.405": "6.7411", 132 | "6.419": "6.3730", 133 | "6.431": "6.3702", 134 | "6.434": "6.7730", 135 | "6.435": "6.7830", 136 | "6.436": "6.7700", 137 | "6.437": "6.7800", 138 | "6.438": "6.7810", 139 | "6.439": "6.3732", 140 | "6.440": "6.7460", 141 | "6.441": "6.7470", 142 | "6.442": "6.7430", 143 | "6.443": "6.6420", 144 | "6.450": "6.7410", 145 | "6.452": "6.7440", 146 | "6.454": "6.9090", 147 | "6.456": "6.7020", 148 | "6.481": "6.3722", 149 | "6.484": "6.8200", 150 | "6.485": "6.7250", 151 | "6.521": "6.4812", 152 | "6.522": "6.4822", 153 | "6.524": "6.4842", 154 | "6.525": "6.4861", 155 | "6.542": "6.8820", 156 | "6.544": "6.8880", 157 | "6.545": "6.8890", 158 | "6.552": "6.8830", 159 | "6.555": "6.8800", 160 | "6.556": "6.8810", 161 | "6.557": "6.4852", 162 | "6.561": "6.4832", 163 | "6.580": "6.8721", 164 | "6.589": "6.8720", 165 | "6.602": "6.6331", 166 | "6.621": "6.6330", 167 | "6.630": "6.6300", 168 | "6.631": "6.6310", 169 | "6.632": "6.6320", 170 | "6.634": "6.6340", 171 | "6.637": "6.6370", 172 | "6.640": "6.6210", 173 | "6.644": "6.6440", 174 | "6.645": "6.6450", 175 | "6.685": "6.6280", 176 | "6.690": "6.2212", 177 | "6.695": "6.6290", 178 | "6.701": "6.2530", 179 | "6.717": "6.2690", 180 | "6.719": "6.2532", 181 | "6.720": "6.6500", 182 | "6.728": "6.6400", 183 | "6.730": "6.6510", 184 | "6.731": "6.6520", 185 | "6.732": "6.6530", 186 | "6.735": "6.6540", 187 | "6.736": "6.6550", 188 | "6.774": "6.6610", 189 | "6.775": "6.6000", 190 | "6.776": "6.6020", 191 | "6.777": "6.2692", 192 | "6.780": "6.6630", 193 | "6.781": "6.6600", 194 | "6.800": "6.4210", 195 | "6.801": "6.8391", 196 | "6.802": "6.8711", 197 | "6.803": "6.4140", 198 | "6.804": "6.4120", 199 | "6.805": "6.4590", 200 | "6.806": "6.8611", 201 | "6.807": "6.4420", 202 | "6.808": "6.1820", 203 | "6.810": "6.4510", 204 | "6.811": "6.4530", 205 | "6.812": "6.5931", 206 | "6.814": "6.5831", 207 | "6.815": "6.8371", 208 | "6.816": "6.5081", 209 | "6.817": "6.4130", 210 | "6.818": "6.1120", 211 | "6.819": "6.8301", 212 | "6.820": "6.5110", 213 | "6.822": "6.5120", 214 | "6.823": "6.5900", 215 | "6.824": "6.5840", 216 | "6.825": "6.5930", 217 | "6.826": "6.5850", 218 | "6.827": "6.5060", 219 | "6.828": "6.5810", 220 | "6.829": "6.5820", 221 | "6.830": "6.5830", 222 | "6.832": "6.8210", 223 | "6.833": "6.4142", 224 | "6.834": "6.8110", 225 | "6.835": "6.8510", 226 | "6.836": "6.5080", 227 | "6.837": "6.4400", 228 | "6.838": "6.8410", 229 | "6.839": "6.8420", 230 | "6.840": "6.5400", 231 | "6.841": "6.5410", 232 | "6.842": "6.5420", 233 | "6.843": "6.4212", 234 | "6.844": "6.4102", 235 | "6.845": "6.5430", 236 | "6.846": "6.5920", 237 | "6.847": "6.5350", 238 | "6.849": "6.5310", 239 | "6.850": "6.5320", 240 | "6.851": "6.5230", 241 | "6.852": "6.5250", 242 | "6.853": "6.5340", 243 | "6.854": "6.5210", 244 | "6.855": "6.5240", 245 | "6.856": "6.5220", 246 | "6.857": "6.5610", 247 | "6.858": "6.5660", 248 | "6.859": "6.8530", 249 | "6.860": "6.7910", 250 | "6.861": "6.8150", 251 | "6.862": "6.3902", 252 | "6.863": "6.8630", 253 | "6.864": "6.8610", 254 | "6.865": "6.8370", 255 | "6.866": "6.8390", 256 | "6.867": "6.7900", 257 | "6.869": "6.8300", 258 | "6.870": "6.8320", 259 | "6.871": "6.7930", 260 | "6.874": "6.8710", 261 | "6.875": "6.5620", 262 | "6.876": "6.5630", 263 | "6.877": "6.4132", 264 | "6.878": "6.8700", 265 | "6.881": "6.8160", 266 | "6.882": "6.8170", 267 | "6.883": "6.8180", 268 | "6.884": "6.8190", 269 | "6.885": "6.5690", 270 | "6.886": "6.5990", 271 | "6.887": "6.5880", 272 | "6.888": "6.5890", 273 | "6.889": "6.5480", 274 | "6.890": "6.5490", 275 | "6.891": "6.5370", 276 | "6.892": "6.5380", 277 | "6.893": "6.5390", 278 | "6.894": "6.8580", 279 | "6.895": "6.8590", 280 | "6.896": "6.8480", 281 | "6.897": "6.8490", 282 | "6.901": "6.9160", 283 | "6.9021": "6.9101", 284 | "6.902A": "6.910A", 285 | "6.902B": "6.910B", 286 | "6.903": "6.9310", 287 | "6.904": "6.9320", 288 | "6.9041": "6.9321", 289 | "6.905": "6.5151", 290 | "6.906": "6.9300", 291 | "6.910": "6.9800", 292 | "6.911": "6.9110", 293 | "6.912": "6.9120", 294 | "6.913": "6.9130", 295 | "6.914": "6.9140", 296 | "6.915": "6.9150", 297 | "6.920": "6.9820", 298 | "6.921": "6.9850", 299 | "6.922": "6.9860", 300 | "6.923": "6.9700", 301 | "6.924": "6.9710", 302 | "6.925": "6.9720", 303 | "6.926": "6.9260", 304 | "6.927": "6.9270", 305 | "6.928": "6.9280", 306 | "6.929": "6.9380", 307 | "6.930": "6.9360", 308 | "6.932": "6.9970", 309 | "6.934": "6.9370", 310 | "6.935": "6.9350", 311 | "6.936": "6.9302", 312 | "6.941": "6.9390", 313 | "6.943": "6.9020", 314 | "6.945": "6.5150", 315 | "6.946": "6.5160", 316 | "6.951": "6.9870", 317 | "6.952": "6.9880", 318 | "6.960": "6.9920", 319 | "6.961": "6.9932", 320 | "6.962": "6.9990", 321 | "6.963": "6.9930", 322 | "6.980": "6.9900", 323 | "6.981": "6.9902", 324 | "6.991": "6.9910", 325 | "6.994": "6.9940", 326 | "6.995": "6.9950", 327 | "6.997": "6.9830", 328 | "6.998": "6.9840" 329 | } 330 | -------------------------------------------------------------------------------- /new_scripts/coursews.py: -------------------------------------------------------------------------------- 1 | import json 2 | import requests 3 | import itertools 4 | 5 | term = '2023FA' 6 | 7 | # copied from csb.py 8 | 9 | timeslots = 30 10 | days = {'M': 0, 11 | 'T': timeslots, 12 | 'W': timeslots * 2, 13 | 'R': timeslots * 3, 14 | 'F': timeslots * 4} 15 | times = {'8': 0, 16 | '8.30': 1, 17 | '9': 2, 18 | '9.30': 3, 19 | '10': 4, 20 | '10.30': 5, 21 | '11': 6, 22 | '11.30': 7, 23 | '12': 8, 24 | '12.30': 9, 25 | '1': 10, 26 | '1.30': 11, 27 | '2': 12, 28 | '2.30': 13, 29 | '3': 14, 30 | '3.30': 15, 31 | '4': 16, 32 | '4.30': 17, 33 | '5': 18, 34 | '5.30': 19, 35 | '6': 20, 36 | '6.30': 21, 37 | '7': 22, 38 | '7.30': 23} 39 | 40 | eve_times = {'12': 8, 41 | '12.30': 9, 42 | '1': 10, 43 | '1.30': 11, 44 | '2': 12, 45 | '2.30': 13, 46 | '3': 14, 47 | '3.30': 15, 48 | '4': 16, 49 | '4.30': 17, 50 | '5': 18, 51 | '5.30': 19, 52 | '6': 20, 53 | '6.30': 21, 54 | '7': 22, 55 | '7.30': 23, 56 | '8': 24, 57 | '8.30': 25, 58 | '9': 26, 59 | '9.30': 27, 60 | '10': 28, 61 | '10.30': 29} 62 | 63 | def tsp_eve(t, number): 64 | wdays = t.split()[0] 65 | t = t[t.find("(")+1:t.find(")")].rstrip(' PM') 66 | 67 | slots = [] 68 | startendtime = t.split('-') 69 | try: 70 | for d in wdays: 71 | if len(startendtime) > 1: 72 | length = eve_times[startendtime[1]] - times[startendtime[0]] 73 | else: 74 | length = 2 75 | slots.append((days[d] + eve_times[startendtime[0]], length)) 76 | except Exception as e: 77 | print("tsp_eve", e, t, number) 78 | 79 | return slots 80 | 81 | def tsp(t, number): 82 | if '*' in t: 83 | return [] 84 | 85 | if 'EVE' in t: 86 | return tsp_eve(t, number) 87 | 88 | slots = [] 89 | try: 90 | t = t.split()[0] 91 | # remove trailing parens e.g. MWF2(LIMITEDTO15) 92 | t = t.split("(")[0] 93 | 94 | for t in t.split(','): 95 | 96 | split = [''.join(x) for _, x in itertools.groupby(t, key=str.isalpha)] 97 | startendtime = split[1].split('-') 98 | 99 | for d in split[0]: 100 | if len(startendtime) > 1: 101 | length = times[startendtime[1]] - times[startendtime[0]] 102 | else: 103 | length = 2 104 | slots.append((days[d] + times[startendtime[0]], length)) 105 | except Exception as e: 106 | print("tsp", e, t, number) 107 | 108 | return slots 109 | 110 | url = f'http://coursews.mit.edu/coursews/?term={term}' 111 | 112 | raw_text = requests.get(url).text 113 | # hilariously, the json is invalid thanks to one class 114 | fixed_text = raw_text.replace('"Making"', '"Making"') 115 | raw_classes = json.loads(fixed_text)['items'] 116 | classes = {} 117 | 118 | terms = {'Fall': 'FA', 'IAP': 'JA', 'Spring': 'SP', 'Summer': 'SU'} 119 | 120 | def term_map(semesters): 121 | return [terms[s] for s in semesters] 122 | 123 | def parse_units(units): 124 | u = units.split('-') 125 | return u[0], u[1], u[2] 126 | 127 | gir_rewrite = { 128 | 'GIR:CAL1': 'Calculus I (GIR)', 129 | 'GIR:CAL2': 'Calculus II (GIR)', 130 | 'GIR:PHY1': 'Physics I (GIR)', 131 | 'GIR:PHY2': 'Physics II (GIR)', 132 | 'GIR:CHEM': 'Chemistry (GIR)', 133 | 'GIR:BIOL': 'Biology (GIR)', 134 | } 135 | 136 | def parse_prereqs(prereqs): 137 | if len(prereqs) == 0: 138 | return 'None' 139 | for gir, gir_rw in gir_rewrite.items(): 140 | prereqs = prereqs.replace(gir, gir_rw) 141 | return prereqs 142 | 143 | def parse_joint(joint): 144 | return ', '.join([j.rstrip('J') for j in joint]) 145 | 146 | instructors = 'fall_instructors' if term[-2:] == 'FA' else 'spring_instructors' 147 | 148 | with open("course_six_renumbering.json") as f: 149 | course_six_renumbering = json.loads(f.read()) 150 | course_six_renumbering_inv = {v: k for k, v in course_six_renumbering.items()} 151 | 152 | for c in raw_classes: 153 | if c['type'] == 'Class': 154 | number = c['id'] 155 | name = c['label'] 156 | 157 | # if number in course_six_renumbering_inv: 158 | # name = "[" + course_six_renumbering_inv[number] + "] " + name 159 | 160 | units1, units2, units3 = parse_units(c['units']) 161 | 162 | classes[number] = { 163 | 'number': number, 164 | 'name':name, 165 | 'course': number.split('.')[0], 166 | 'class': number.split('.')[1], 167 | 'sections': [], 168 | 'l': [], 169 | 'r': [], 170 | 'b': [], 171 | 'l_raw': [], 172 | 'r_raw': [], 173 | 'b_raw': [], 174 | 'tba': False, 175 | 'all_slots': [], 176 | 'level': ('U' if c['level'] == 'Undergraduate' else 'G'), 177 | 'terms': term_map(c['semester']), 178 | 'desc': c['description'], 179 | 'units1': int(units1), 180 | 'units2': int(units2), 181 | 'units3': int(units3), 182 | 'total_units': int(c['total-units']), 183 | 'REST': c['gir_attribute'] == 'REST', 184 | 'LAB': c['gir_attribute'] == 'LAB', 185 | 'pLAB': c['gir_attribute'] == 'LAB2', 186 | 'CI-H': c['comm_req_attribute'] == 'CIH', 187 | 'CI-HW': c['comm_req_attribute'] == 'CIHW', 188 | 'CI-M': c['comm_req_attribute'] == 'CIM', 189 | 'HASS-H': 'HH' in c['hass_attribute'], 190 | 'HASS-A': 'HA' in c['hass_attribute'], 191 | 'HASS-S': 'HS' in c['hass_attribute'], 192 | 'HASS-E': 'HE' in c['hass_attribute'], 193 | 'prereq': parse_prereqs(c['prereqs']), 194 | 'same_as': parse_joint(c['joint_subjects']), 195 | 'meets_with': parse_joint(c['meets_with_subjects']), 196 | 'sat': False, 197 | 'limited': 'limited' in c['description'].lower(), 198 | # 'instructors': ', '.join(c[instructors]), 199 | 'in-charge': c['in-charge']} 200 | 201 | for c in raw_classes: 202 | if c['type'] == 'Class': 203 | continue 204 | elif c['type'] == 'LectureSession': 205 | typ = 'l' 206 | typraw = 'l_raw' 207 | elif c['type'] == 'RecitationSession': 208 | typ = 'r' 209 | typraw = 'r_raw' 210 | elif c['type'] == 'LabSession': 211 | typ = 'b' 212 | typraw = 'b_raw' 213 | else: 214 | print("unknown type", c) 215 | continue 216 | 217 | number = c['section-of'] 218 | 219 | if number not in classes: 220 | print('section for nonexistent class', number) 221 | continue 222 | 223 | cl = classes[number] 224 | 225 | if 'EVE' in c['timeAndPlace']: 226 | split = c['timeAndPlace'].rsplit(' ', 1) 227 | t = split[0] 228 | else: 229 | # For some reason, a couple classes are totally inconsistent. 230 | tp = c['timeAndPlace'] 231 | if ":00" in tp or ":30" in tp: 232 | tp = tp.replace(":00", "").replace(":30", ".30") 233 | split = tp.rsplit(' ', 1) 234 | t = split[0].replace(' ', '') 235 | 236 | # e.g. F1 (BEGINS OCT 31) 237 | if 'ENDS' in t or 'BEGINS' in t: 238 | t = t.split('(')[0].strip() 239 | 240 | p = split[1] 241 | 242 | if p == 'VIRTUAL': 243 | p = 'Virtual' 244 | 245 | # Check for TBA. 246 | if t == '*TO BE ARRANGED' or t == 'null' or t.lower() == 'tbd' or t.lower() == 'tba' or t == '*TOBEARRANGED': 247 | cl['tba'] = True 248 | continue 249 | 250 | # Check for Saturday :( 251 | if 'S' in t: 252 | cl['sat'] = True 253 | continue 254 | 255 | # Parse timeslot. 256 | # Format: 30 timeslots a day. 257 | slots = [] 258 | for s in t.strip().split(','): 259 | slots.extend(tsp(s, number)) 260 | 261 | # If no slots, ignore. 262 | if slots == []: 263 | continue 264 | 265 | # If duplicate slot, throw out. 266 | if slots in cl['all_slots']: 267 | continue 268 | else: 269 | cl['all_slots'].append(slots) 270 | 271 | slots = (slots, p) 272 | 273 | if typ not in cl['sections']: 274 | cl['sections'].append(typ) 275 | 276 | cl[typ].append(slots) 277 | cl[typraw].append(t) 278 | 279 | with open('ws', 'w') as f: 280 | json.dump(classes, f) 281 | 282 | with open('all_classes', 'w') as f: 283 | json.dump(list(classes.keys()), f) 284 | -------------------------------------------------------------------------------- /new_scripts/csb.py: -------------------------------------------------------------------------------- 1 | import string 2 | import itertools 3 | import json 4 | 5 | timeslots = 30 6 | days = {'M': 0, 7 | 'T': timeslots, 8 | 'W': timeslots * 2, 9 | 'R': timeslots * 3, 10 | 'F': timeslots * 4} 11 | times = {'8': 0, 12 | '8.30': 1, 13 | '9': 2, 14 | '9.30': 3, 15 | '10': 4, 16 | '10.30': 5, 17 | '11': 6, 18 | '11.30': 7, 19 | '12': 8, 20 | '12.30': 9, 21 | '1': 10, 22 | '1.30': 11, 23 | '2': 12, 24 | '2.30': 13, 25 | '3': 14, 26 | '3.30': 15, 27 | '4': 16, 28 | '4.30': 17, 29 | '5': 18, 30 | '5.30': 19, 31 | '6': 20, 32 | '6.30': 21, 33 | '7': 22, 34 | '7.30': 23} 35 | 36 | eve_times = {'1': 10, 37 | '1.30': 11, 38 | '2': 12, 39 | '2.30': 13, 40 | '3': 14, 41 | '3.30': 15, 42 | '4': 16, 43 | '4.30': 17, 44 | '5': 18, 45 | '5.30': 19, 46 | '6': 20, 47 | '6.30': 21, 48 | '7': 22, 49 | '7.30': 23, 50 | '8': 24, 51 | '8.30': 25, 52 | '9': 26, 53 | '9.30': 27, 54 | '10': 28, 55 | '10.30': 29} 56 | 57 | def tsp_eve(t): 58 | wdays = t.split()[0] 59 | t = t[t.find("(")+1:t.find(")")].rstrip(' PM') 60 | 61 | slots = [] 62 | startendtime = t.split('-') 63 | try: 64 | for d in wdays: 65 | if len(startendtime) > 1: 66 | length = eve_times[startendtime[1]] - times[startendtime[0]] 67 | else: 68 | length = 2 69 | slots.append((days[d] + eve_times[startendtime[0]], length)) 70 | except Exception: 71 | print(t) 72 | 73 | return slots 74 | 75 | def tsp(t): 76 | if '*' in t: 77 | return [] 78 | 79 | if 'EVE' in t: 80 | return tsp_eve(t) 81 | 82 | t = t.split()[0] 83 | slots = [] 84 | 85 | try: 86 | for t in t.split(','): 87 | 88 | split = [''.join(x) for _, x in itertools.groupby(t, key=str.isalpha)] 89 | startendtime = split[1].split('-') 90 | 91 | for d in split[0]: 92 | if len(startendtime) > 1: 93 | length = times[startendtime[1]] - times[startendtime[0]] 94 | else: 95 | length = 2 96 | slots.append((days[d] + times[startendtime[0]], length)) 97 | except Exception: 98 | print(t) 99 | 100 | return slots 101 | 102 | f = open('csb_raw') 103 | 104 | current_class = '' 105 | classes = {} 106 | 107 | f.readline() 108 | 109 | for line in f: 110 | c = line.replace('"', '').split('\t') 111 | c = [x.strip() for x in c] 112 | 113 | # Check that this line is actually a class. 114 | try: 115 | if not c[0][0].isalnum() or len(c) < 3: 116 | continue 117 | except: 118 | print(c) 119 | continue 120 | 121 | # Check that the class hasn't been cancelled. 122 | if "SUBJECT CANCELLED" in c[3]: 123 | continue 124 | 125 | # Initialize class object. 126 | if c[0] not in classes: 127 | classes[c[0]] = {'number': c[0], 128 | 'course': c[0].split('.')[0], 129 | 'class': c[0].split('.')[1], 130 | 'sections': [], 131 | 'l': [], 132 | 'r': [], 133 | 'b': [], 134 | 'l_raw': [], 135 | 'r_raw': [], 136 | 'b_raw': [], 137 | 'final': False, 138 | 'tba': False, 139 | 'all_slots': []} 140 | 141 | # Throw out MS/NS because their times are weird. 142 | if c[0][:2] in ['MS', 'NS']: 143 | continue 144 | 145 | # Check for finals. 146 | if c[1] == 'Q01': 147 | classes[c[0]]['final'] = True 148 | continue 149 | 150 | # Check for TBA. 151 | if c[3] == '*TO BE ARRANGED\n': 152 | classes[c[0]]['tba'] = True 153 | continue 154 | 155 | # Throw out comments. 156 | if c[1][0] == '0': 157 | continue 158 | 159 | # Parse timeslot. 160 | # Format: 30 timeslots a day. 161 | slots = [] 162 | for s in c[3].strip().split(','): 163 | slots.extend(tsp(s)) 164 | 165 | # If no slots, ignore. 166 | if slots == []: 167 | continue 168 | 169 | # If duplicate slot, throw out. 170 | if slots in classes[c[0]]['all_slots']: 171 | continue 172 | else: 173 | classes[c[0]]['all_slots'].append(slots) 174 | 175 | slots = (slots, c[2]) 176 | 177 | if c[1][0] == 'L': 178 | if 'l' not in classes[c[0]]['sections']: 179 | classes[c[0]]['sections'].append('l') 180 | classes[c[0]]['l'].append(slots) 181 | classes[c[0]]['l_raw'].append(c[3].strip()) 182 | elif c[1][0] == 'R': 183 | if 'r' not in classes[c[0]]['sections']: 184 | classes[c[0]]['sections'].append('r') 185 | classes[c[0]]['r'].append(slots) 186 | classes[c[0]]['r_raw'].append(c[3].strip()) 187 | else: 188 | if 'b' not in classes[c[0]]['sections']: 189 | classes[c[0]]['sections'].append('b') 190 | classes[c[0]]['b'].append(slots) 191 | classes[c[0]]['b_raw'].append(c[3].strip()) 192 | 193 | with open('csb', 'w') as f: 194 | json.dump(classes, f) 195 | 196 | with open('all_classes', 'w') as f: 197 | json.dump(list(classes.keys()), f) 198 | 199 | -------------------------------------------------------------------------------- /new_scripts/csb_to_full.sh: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | python xlsx2tsv.py CSB.xlsx 1 > csb_raw 3 | python csb.py 4 | python sublist2.py 5 | python combiner.py 6 | cp full.json ../www/full.js 7 | open ../www/index.html -------------------------------------------------------------------------------- /new_scripts/sublist.py: -------------------------------------------------------------------------------- 1 | import json 2 | import requests 3 | import re 4 | from bs4 import BeautifulSoup 5 | 6 | base_url = 'http://catalog.mit.edu/subjects/' 7 | 8 | courses = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 14, 15, 16, 17, 18, 20, 22, 24, 'AS', 'EC', 9 | 'MS', 'NS', '21A', '21G', '21H', '21L', '21M', '21W', 'CMS', 'CON', 'CSB', 'ESD', 10 | 'ESG', 'HST', 'ISP', 'MAS', 'STS', 'WGS'] 11 | 12 | with open('all_classes') as f: 13 | class_list = json.load(f) 14 | 15 | classes = {} 16 | 17 | for c in courses: 18 | r = requests.get(base_url + str(c).lower()) 19 | soup = BeautifulSoup(r.content, "lxml") 20 | 21 | cblocks = soup.find_all("div", class_= "courseblock") 22 | 23 | for cb in cblocks: 24 | title = cb.find("h4", class_="courseblocktitle").text 25 | if title.split()[0].rstrip('[J]') not in class_list: 26 | continue 27 | 28 | num = title.split()[0].rstrip('[J]') 29 | 30 | classes[num] = {'name': ' '.join(title.split()[1:]), 31 | 'level': '', 32 | 'terms': [], 33 | 'desc': '', 34 | 'units1': 0, 35 | 'units2': 0, 36 | 'units3': 0, 37 | 'total_units': 0, 38 | 'REST': False, 39 | 'LAB': False, 40 | 'CI-H': False, 41 | 'CI-HW': False, 42 | 'HASS-H': False, 43 | 'HASS-A': False, 44 | 'HASS-S': False, 45 | 'HASS-E': False} 46 | 47 | terms = cb.find("span", class_="courseblockterms").text 48 | if 'U ' in terms: 49 | classes[num]['level'] = 'U' 50 | elif 'G ' in terms: 51 | classes[num]['level'] = 'G' 52 | 53 | if 'Fall' in terms: 54 | classes[num]['terms'].append('FA') 55 | if 'IAP' in terms: 56 | classes[num]['terms'].append('JA') 57 | if 'Spring' in terms: 58 | classes[num]['terms'].append('SP') 59 | if 'Summer' in terms: 60 | classes[num]['terms'].append('SU') 61 | 62 | hours = cb.find("span", class_="courseblockhours").text 63 | units = hours.split()[0].split('-') 64 | if len(units) == 3: 65 | classes[num]['units1'] = int(units[0]) 66 | classes[num]['units2'] = int(units[1]) 67 | classes[num]['units3'] = int(units[2]) 68 | classes[num]['total_units'] = classes[num]['units1'] + classes[num]['units2'] + classes[num]['units3'] 69 | 70 | if 'REST' in hours: 71 | classes[num]['REST'] = True 72 | if 'LAB' in hours: 73 | classes[num]['LAB'] = True 74 | if 'CI-HW' in hours: 75 | classes[num]['CI-HW'] = True 76 | if 'CI-H' in hours: 77 | classes[num]['CI-H'] = True 78 | if 'HASS-H' in hours: 79 | classes[num]['HASS-H'] = True 80 | if 'HASS-A' in hours: 81 | classes[num]['HASS-A'] = True 82 | if 'HASS-S' in hours: 83 | classes[num]['HASS-S'] = True 84 | if 'HASS-E' in hours: 85 | classes[num]['HASS-E'] = True 86 | 87 | desc = cb.find("p", class_="courseblockdesc").text 88 | classes[num]['desc'] = desc.strip() 89 | 90 | print(num) 91 | 92 | with open("sublist", 'w') as f: 93 | json.dump(classes, f) 94 | 95 | 96 | 97 | -------------------------------------------------------------------------------- /new_scripts/sublist2.py: -------------------------------------------------------------------------------- 1 | import json 2 | import requests 3 | import re 4 | from bs4 import BeautifulSoup 5 | 6 | base_url = "http://student.mit.edu/catalog/search.cgi?search=" 7 | 8 | with open('all_classes') as f: 9 | class_list = json.load(f) 10 | 11 | classes = {} 12 | 13 | for c in class_list: 14 | try: 15 | while True: 16 | try: 17 | r = requests.get(base_url + c, timeout=5) 18 | break 19 | except requests.exceptions.ConnectionError: 20 | pass 21 | soup = BeautifulSoup(r.content, "lxml") 22 | 23 | start = soup.h3 24 | num = c 25 | 26 | classes[num] = {'name': ' '.join(start.text.strip().split()[1:]), 27 | 'level': 'U', 28 | 'terms': [], 29 | 'desc': '', 30 | 'times': '', 31 | 'units1': 0, 32 | 'units2': 0, 33 | 'units3': 0, 34 | 'total_units': 0, 35 | 'no_next': False, 36 | 'repeat': False, 37 | 'REST': False, 38 | 'LAB': False, 39 | 'pLAB': False, 40 | 'CI-H': False, 41 | 'CI-HW': False, 42 | 'HASS-H': False, 43 | 'HASS-A': False, 44 | 'HASS-S': False, 45 | 'HASS-E': False, 46 | 'prereq': 'None', 47 | 'same_as': '', 48 | 'meets_with': '', 49 | 'url': ''} 50 | 51 | level = start.findNext('img').findNext('img') 52 | 53 | if 'nonext' in str(level): 54 | classes[num]['no_next'] = True 55 | level = level.findNext('img') 56 | 57 | if 'Undergrad' in str(level): 58 | classes[num]['level'] = 'U' 59 | elif 'Graduate' in str(level): 60 | classes[num]['level'] = 'G' 61 | 62 | gterms = ['Fall', 'IAP', 'Spring', 'Summer'] 63 | terms = [level.findNext()] 64 | while True: 65 | next_term = terms[-1].findNext() 66 | if not any(x in str(next_term) for x in gterms): 67 | break 68 | terms.append(next_term) 69 | 70 | for term in terms: 71 | if 'Fall' in str(term): 72 | classes[num]['terms'].append('FA') 73 | if 'IAP' in str(term): 74 | classes[num]['terms'].append('JA') 75 | if 'Spring' in str(term): 76 | classes[num]['terms'].append('SP') 77 | if 'Summer' in str(term): 78 | classes[num]['terms'].append('SU') 79 | 80 | others = [terms[-1]] 81 | while True: 82 | next_other = others[-1].findNext() 83 | if str(next_other) == '
': 84 | break 85 | others.append(next_other) 86 | 87 | for other in others: 88 | if 'repeat.gif' in str(other): 89 | classes[num]['repeat'] = True 90 | if 'rest.gif' in str(other): 91 | classes[num]['REST'] = True 92 | if 'PartLab.gif' in str(other): 93 | classes[num]['pLAB'] = True 94 | elif 'Lab.gif' in str(other): 95 | classes[num]['LAB'] = True 96 | if 'cihw.gif' in str(other): 97 | classes[num]['CI-HW'] = True 98 | if 'cih1.gif' in str(other): 99 | classes[num]['CI-H'] = True 100 | if 'hassH' in str(other): 101 | classes[num]['HASS-H'] = True 102 | if 'hassA' in str(other): 103 | classes[num]['HASS-A'] = True 104 | if 'hassS' in str(other): 105 | classes[num]['HASS-S'] = True 106 | if 'hassE' in str(other) or 'hassT' in str(other): 107 | classes[num]['HASS-E'] = True 108 | 109 | hours = soup.body.findAll(text=re.compile('Units'))[0].strip().split()[1] 110 | units = hours.split()[0].split('-') 111 | if len(units) == 3: 112 | classes[num]['units1'] = int(units[0]) 113 | classes[num]['units2'] = int(units[1]) 114 | classes[num]['units3'] = int(units[2]) 115 | classes[num]['total_units'] = classes[num]['units1'] + classes[num]['units2'] + classes[num]['units3'] 116 | 117 | prereq = soup.getText().split('Prereq:') 118 | if len(prereq) > 1: 119 | classes[num]['prereq'] = prereq[1].split('\n')[0].strip() 120 | 121 | same = soup.getText().split('Same subject as ') 122 | if len(same) > 1: 123 | same_as = same[1].split(')')[0].split(',') 124 | classes[num]['same_as'] = ', '.join(x.strip(' ,[J]') for x in same_as) 125 | 126 | meets = soup.getText().split('Subject meets with ') 127 | if len(meets) > 1: 128 | meets_with = meets[1].split(')')[0].split(',') 129 | classes[num]['meets_with'] = ', '.join(x.strip(' ,[J]') for x in meets_with) 130 | 131 | url = soup.getText().split('URL: ') 132 | if len(url) > 1: 133 | classes[num]['url'] = url[1].split('\n')[0].strip('?') 134 | 135 | desc = others[-1].findNext("img") 136 | while 'hr.gif' not in str(desc): 137 | desc = desc.findNext("img") 138 | 139 | desc = desc.findNext().nextSibling 140 | 141 | if desc != None: 142 | classes[num]['desc'] = desc.strip() 143 | 144 | print(num) 145 | except (AttributeError, TypeError) as e: 146 | print("Failed:", num) 147 | print(e) 148 | 149 | with open("sublist", 'w') as f: 150 | json.dump(classes, f) 151 | -------------------------------------------------------------------------------- /new_scripts/sublist_ws.py: -------------------------------------------------------------------------------- 1 | import json 2 | import requests 3 | import re 4 | from bs4 import BeautifulSoup 5 | 6 | base_url = "http://student.mit.edu/catalog/search.cgi?search=" 7 | 8 | with open('all_classes') as f: 9 | class_list = json.load(f) 10 | 11 | classes = {} 12 | 13 | for c in class_list: 14 | try: 15 | while True: 16 | try: 17 | r = requests.get(base_url + c, timeout=5) 18 | break 19 | except requests.exceptions.ConnectionError: 20 | pass 21 | soup = BeautifulSoup(r.content, "lxml") 22 | 23 | start = soup.h3 24 | num = c 25 | 26 | classes[num] = {'no_next': False, 27 | 'repeat': False, 28 | 'half': False, 29 | 'url': ''} 30 | 31 | name_split = start.getText().split("\n") 32 | if len(name_split) > 2 and name_split[1] != "(New)": 33 | classes[num]['old_num'] = name_split[1][1:-1] 34 | 35 | level = start.findNext('img').findNext('img') 36 | 37 | if 'nonext' in str(level): 38 | classes[num]['no_next'] = True 39 | level = level.findNext('img') 40 | 41 | if 'Undergrad' in str(level): 42 | classes[num]['level'] = 'U' 43 | elif 'Graduate' in str(level): 44 | classes[num]['level'] = 'G' 45 | 46 | gterms = ['Fall', 'IAP', 'Spring', 'Summer'] 47 | terms = [level.findNext()] 48 | while True: 49 | next_term = terms[-1].findNext() 50 | if not any(x in str(next_term) for x in gterms): 51 | break 52 | terms.append(next_term) 53 | 54 | others = [terms[-1]] 55 | while True: 56 | next_other = others[-1].findNext() 57 | if str(next_other) == '
': 58 | break 59 | others.append(next_other) 60 | 61 | for other in others: 62 | if 'repeat.gif' in str(other): 63 | classes[num]['repeat'] = True 64 | 65 | half = soup.getText().split(' half of term') 66 | if len(half) > 1: 67 | term = half[0].split(' ')[-1].strip() 68 | if term == "first": 69 | classes[num]['half'] = 1 70 | if term == "second": 71 | classes[num]['half'] = 2 72 | 73 | url = soup.getText().split('URL: ') 74 | if len(url) > 1: 75 | classes[num]['url'] = url[1].split('\n')[0].strip('?') 76 | 77 | classes[num]['final'] = '+final' in soup.getText() 78 | 79 | print(num) 80 | except (AttributeError, TypeError) as e: 81 | print("Failed:", num) 82 | print(e) 83 | 84 | with open("sublist", 'w') as f: 85 | json.dump(classes, f) 86 | -------------------------------------------------------------------------------- /new_scripts/update_schedule.sh: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | cd "${0%/*}" 3 | echo "=== coursews.py ===" 4 | python3 coursews.py 5 | echo "=== sublist_ws.py ===" 6 | python3 sublist_ws.py 7 | echo "=== combiner_ws.py ===" 8 | python3 combiner_ws.py 9 | cp full.js ../www/full.js 10 | -------------------------------------------------------------------------------- /new_scripts/xlsx2tsv.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """ 3 | xlsx2tsv filename.xlsx [sheet number or name] 4 | 5 | Parse a .xlsx (Excel OOXML, which is not OpenOffice) into tab-separated values. 6 | If it has multiple sheets, need to give a sheet number or name. 7 | Outputs honest-to-goodness tsv, no quoting or embedded \\n\\r\\t. 8 | 9 | One reason I wrote this is because Mac Excel 2008 export to csv or tsv messes 10 | up encodings, converting everything to something that's not utf8 (macroman 11 | perhaps). This script seems to do better. 12 | 13 | The spec for this format is 5220 pages. I did not use it. This was helpful: 14 | http://blogs.msdn.com/excel/archive/2008/08/14/reading-excel-files-from-linux.aspx 15 | But mostly I guessed how the format works. So bugs are guaranteed. 16 | 17 | brendan o'connor - anyall.org - gist.github.com/22764 18 | """ 19 | 20 | #from __future__ import print_function 21 | import xml.etree.ElementTree as ET 22 | import os,sys,zipfile,re,itertools 23 | 24 | def myjoin(seq, sep=" "): 25 | " because str.join() is annoying " 26 | return sep.join(str(x) for x in seq) 27 | 28 | args = sys.argv[:] 29 | args.pop(0) 30 | if args: 31 | z = zipfile.ZipFile(args.pop(0)) 32 | elif not sys.stdin.isatty(): 33 | z = zipfile.ZipFile(sys.stdin) 34 | else: 35 | print __doc__.strip() 36 | sys.exit(1) 37 | 38 | n=lambda x: "{http://schemas.openxmlformats.org/spreadsheetml/2006/main}%s" % x 39 | 40 | sheet_filenames = [f for f in z.namelist() if re.search("^xl/worksheets/sheet.*xml$", f)] 41 | workbook_x = ET.XML(z.read("xl/workbook.xml")) 42 | sheet_xs = workbook_x.find(n("sheets")).findall(n("sheet")) 43 | 44 | def sheet_report(): 45 | global sheet_xs 46 | print>>sys.stderr, "Sheets in this file:" 47 | for i,x in enumerate(sheet_xs): 48 | print>>sys.stderr, "%3d: %s" % (i+1, x.get('name')) 49 | sys.exit(1) 50 | 51 | def sheet_error(msg): 52 | print>>sys.stderr, msg 53 | sheet_report() 54 | 55 | if not args and len(sheet_filenames) > 1: 56 | sheet_error("There are multiple sheets -- need to specify a sheet number or name.") 57 | elif not args and len(sheet_filenames) == 1: 58 | sheet_num = 1 59 | elif args: 60 | sheet_num = args.pop(0) 61 | 62 | if isinstance(sheet_num,str) and (not re.search('^[0-9]+$',sheet_num) or int(sheet_num) > len(sheet_filenames)): 63 | name = sheet_num 64 | inds = [i for i,x in enumerate(sheet_xs) if x.get('name')==name] 65 | if not inds: sheet_error("Can't find sheet with name '%s'" % name) 66 | if len(inds)>1: sheet_error("Multiple sheets with name '%s'" % name) 67 | sheet_num = inds[0] + 1 68 | 69 | 70 | def letter2col_index(letter): 71 | """ A -> 0, B -> 1, Z -> 25, AA -> 26, BA -> 52 """ 72 | base26digits = [1+ord(x)-ord("A") for x in letter] 73 | return sum([x*26**(len(base26digits) - k - 1) for k,x in enumerate(base26digits)]) - 1 74 | 75 | def flatten(iter): 76 | return list(itertools.chain(*iter)) 77 | 78 | def cell2text(cell): 79 | if cell is None: 80 | return "" 81 | elif 't' in cell.attrib and cell.attrib['t'] == 's': 82 | # shared string 83 | idx = int(cell.find(n("v")).text) 84 | si = ss_list[idx] 85 | t_elt = si.find(n("t")) 86 | if t_elt is not None: 87 | return t_elt.text 88 | t_elts = si.findall(n("r") + "/" + n("t")) 89 | if t_elts: 90 | text = "".join( (t.text) for t in t_elts ) 91 | return text 92 | raise Exception("COULDNT DECODE CELL: %s" % ET.tostring(si)) 93 | #return si.find(n("t")).text 94 | #return ET.tostring(si) 95 | else: 96 | v_elt = cell.find(n("v")) 97 | if v_elt is None: return "" 98 | return v_elt.text 99 | 100 | 101 | ss_xml = z.read("xl/sharedStrings.xml") 102 | ss_list = ET.XML(ss_xml).findall(n("si")) 103 | 104 | xml = z.read("xl/worksheets/sheet%s.xml" % sheet_num) 105 | s = ET.fromstring(xml) 106 | rows = s.findall(n("sheetData")+"/"+n("row")) 107 | 108 | all_cells = flatten( [[c for c in row.findall(n("c"))] for row in rows] ) 109 | max_col = max(letter2col_index(re.search("^[A-Z]+",c.attrib['r']).group()) for c in all_cells) 110 | 111 | def make_cells(): 112 | return [None] * (max_col+1) 113 | 114 | warning_count=0 115 | warning_max = 50 116 | def warning(s): 117 | global warning_count 118 | warning_count += 1 119 | if warning_count > warning_max: return 120 | print>>sys.stderr, "WARNING: %s" % s 121 | 122 | def cell_text_clean(text): 123 | s = text.encode("utf-8") 124 | if "\t" in s: warning("Clobbering embedded tab") 125 | if "\n" in s: warning("Clobbering embedded newline") 126 | if "\r" in s: warning("Clobbering embedded carriage return") 127 | s = s.replace("\t"," ").replace("\n"," ").replace("\r"," ") 128 | return s 129 | 130 | for row in rows: 131 | cells_elts = row.findall(n("c")) 132 | inds = [] # parallel 133 | for c in cells_elts: 134 | letter = re.search("^[A-Z]+", c.attrib['r']).group() 135 | inds.append(letter2col_index(letter) ) 136 | cells = make_cells() 137 | for c,j in zip(cells_elts,inds): 138 | cells[j] = c 139 | #print( *(cell2text( c ).encode("utf-8").replace("\t"," ") for c in cells), sep="\t") 140 | print myjoin((cell_text_clean(cell2text( c )) for c in cells), sep="\t") 141 | 142 | if warning_count > warning_max: 143 | print>>sys.stderr, "%d total warnings, %d hidden" % (warning_count, warning_count-warning_max) 144 | -------------------------------------------------------------------------------- /old_scripts/eval_scraper.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python3 2 | 3 | import selenium 4 | from selenium import webdriver 5 | from selenium.webdriver.support import expected_conditions as EC 6 | from selenium.webdriver.support.ui import WebDriverWait 7 | from selenium.common.exceptions import NoSuchElementException 8 | 9 | import os 10 | import time 11 | import pickle 12 | from decimal import * 13 | 14 | username = 'edwardf' 15 | with open('password', 'r') as f: 16 | password = f.read().strip() 17 | 18 | eval_url = 'https://edu-apps.mit.edu/ose-rpt/subjectEvaluationSearch.htm' 19 | courses = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 14, 15, 16, 17, 18, 20, 22, 24, 'AS', 'EC', 20 | 'MS', 'NS', '21A', '21G', '21H', '21L', '21M', '21W', 'CDO', 'CMS', 'CON', 'CSB', 'ESD', 21 | 'ESG', 'HST', 'ISP', 'MAS', 'SCM', 'STS', 'WGS'] 22 | 23 | terms = ['2019FA', '2019JA'] 24 | 25 | def url_from_course(course): 26 | return eval_url + '?departmentId={:+>4}&search=Search'.format(course) 27 | 28 | def url_from_term(term): 29 | return eval_url + '?termId={}&search=Search'.format(term) 30 | 31 | def mit_duo_login(): 32 | session = webdriver.Chrome() 33 | session.get(eval_url) 34 | 35 | # Fill in Kerberos login form 36 | session.find_element_by_name('j_username').send_keys(username) 37 | session.find_element_by_name('j_password').send_keys(password) 38 | 39 | session.find_element_by_name('Submit').click() 40 | 41 | # Send push Duo authentication 42 | time.sleep(5) 43 | WebDriverWait(session, 10).until(EC.frame_to_be_available_and_switch_to_it('duo_iframe')) 44 | session.find_element_by_xpath('//*[contains(text(), "Send Me a Push")]').click() 45 | 46 | # Wait until base page loads 47 | WebDriverWait(session, 60).until(EC.title_is('Subject Evaluation Report Search')) 48 | 49 | return session 50 | 51 | def scrape_class_info(session, class_element, class_dict, term): 52 | # Click link and wait until report loads 53 | url = class_element.get_attribute('href') 54 | class_element.click() 55 | WebDriverWait(session, 60).until(EC.title_contains('Report for')) 56 | 57 | # Classes can have different numbers & different names 58 | titles = session.find_element_by_xpath("/html/body/div[@id='contentsframe']/table[@class='header']/tbody/tr[1]/td[@class='subjectTitle']/h1").text.split('\n') 59 | 60 | class_numbers = [t.split(' ')[0] for t in titles] 61 | class_names = [' '.join(t.split(' ')[1:]) for t in titles] 62 | 63 | # Add static ratings/stats to dict 64 | cd = {} 65 | cd['term'] = term 66 | cd['url'] = url 67 | 68 | try: 69 | 70 | cd['rating'] = Decimal(session.find_element_by_xpath("/html/body/div[@id='contentsframe']/table[@class='header']/tbody/tr[2]/td[@class='summaryContainer']/table[@class='summary']/tbody/tr/td[4]/p").text[27:30]) 71 | 72 | cd['ic_hours'] = Decimal(session.find_element_by_xpath("/html/body/div[@id='contentsframe']/table[@class='indivQuestions'][3]/tbody/tr[4]/td[@class='avg']").text) 73 | 74 | cd['oc_hours'] = Decimal(session.find_element_by_xpath("/html/body/div[@id='contentsframe']/table[@class='indivQuestions'][3]/tbody/tr[5]/td[@class='avg']").text) 75 | 76 | cd['eligible'] = int(session.find_element_by_xpath("/html/body/div[@id='contentsframe']/table[@class='header']/tbody/tr[2]/td[@class='summaryContainer']/table[@class='summary']/tbody/tr/td[1]").text.split()[3]) 77 | 78 | cd['resp'] = int(session.find_element_by_xpath("/html/body/div[@id='contentsframe']/table[@class='header']/tbody/tr[2]/td[@class='summaryContainer']/table[@class='summary']/tbody/tr/td[2]").text.split()[4]) 79 | 80 | cd['rate'] = Decimal(session.find_element_by_xpath("/html/body/div[@id='contentsframe']/table[@class='header']/tbody/tr[2]/td[@class='summaryContainer']/table[@class='summary']/tbody/tr/td[3]").text.split()[2].rstrip('%')) 81 | 82 | except: 83 | # Something went super wrong. 84 | print('{} had a major error.'.format(class_numbers[0])) 85 | session.execute_script("window.history.go(-1)") 86 | WebDriverWait(session, 60).until(EC.title_is('Search Results')) 87 | return 88 | 89 | # Add professors to dict 90 | cd['professors'] = [] 91 | 92 | try: 93 | for x in range(3, 100): 94 | prof = {} 95 | 96 | prof['name'] = session.find_element_by_xpath("/html/body/div[@id='contentsframe']/table[@class='grid']/tbody/tr[{}]/td[1]/a/strong".format(x)).text 97 | 98 | prof['rating'] = Decimal(session.find_element_by_xpath("/html/body/div[@id='contentsframe']/table[@class='grid']/tbody/tr[{}]/td[5]/span[@class='avg']".format(x)).text) 99 | 100 | prof['role'] = session.find_element_by_xpath("/html/body/div[@id='contentsframe']/table[@class='grid']/tbody/tr[{}]/td[1]".format(x)).text.replace(prof['name'], '').rstrip('(RLECABDGMO1234567890)').strip(' ,') 101 | 102 | cd['professors'].append(prof) 103 | 104 | except NoSuchElementException: 105 | pass 106 | 107 | 108 | # Fits perfectly in a cmd window 109 | print('{:8} | {:48.48} | {} | {} | {}'.format(class_numbers[0], class_names[0], cd['rating'], cd['ic_hours'], cd['oc_hours'])) 110 | 111 | for i in range(len(titles)): 112 | class_num_split = class_numbers[i].split('.') 113 | cd['course_number'] = class_num_split[0] 114 | cd['class_number'] = class_num_split[1] 115 | cd['class_name'] = class_names[i] 116 | 117 | class_dict[class_numbers[i]] = cd 118 | 119 | # Error-resistant back, then wait for search page 120 | session.execute_script("window.history.go(-1)") 121 | WebDriverWait(session, 60).until(EC.title_is('Search Results')) 122 | 123 | def main(): 124 | session = mit_duo_login() 125 | 126 | if not os.path.exists('data'): 127 | os.makedirs('data') 128 | 129 | for term in terms: 130 | class_dict = {} 131 | 132 | print('\n\n\n'.format(term)) 133 | session.get(url_from_term(term)) 134 | 135 | # To cover all possible errors, just keep trying links 136 | for i in range(4, 2000): 137 | try: 138 | class_element = session.find_element_by_xpath("/html/body/div[@id='wrapper']/div[@id='rh-col']/p[{}]/a".format(i)) 139 | scrape_class_info(session, class_element, class_dict, term) 140 | except NoSuchElementException: 141 | continue 142 | 143 | if class_dict != {}: 144 | with open('data/' + term, 'wb') as f: 145 | pickle.dump(class_dict, f) 146 | 147 | if __name__ == '__main__': 148 | main() 149 | 150 | 151 | 152 | 153 | -------------------------------------------------------------------------------- /old_scripts/eval_to_json.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python3 2 | 3 | import simplejson as json 4 | import pickle 5 | import os 6 | import re 7 | from decimal import * 8 | 9 | def atoi(text): 10 | return int(text) if text.isdigit() else text 11 | 12 | def natural_keys(d): 13 | text = next(iter(d.keys())) 14 | return [atoi(c) for c in re.split('(\d+)', text)] 15 | 16 | def main(): 17 | classes = {} 18 | professors = set() 19 | 20 | # Pull all terms into a single list for each class 21 | for term in os.listdir('../../firehose-priv/data/'): 22 | with open('../../firehose-priv/data/' + term, 'rb') as f: 23 | term_dict = pickle.load(f) 24 | for k, v in term_dict.items(): 25 | if k not in classes: 26 | classes[k] = [] 27 | for prof in v['professors']: 28 | professors.add(prof['name']) 29 | classes[k].append(v) 30 | 31 | with open('evaluations.json', 'w') as f: 32 | f.write('var evals = ') 33 | json.dump(classes, f) 34 | f.write(';') 35 | 36 | with open('../new_scripts/evaluations', 'w') as f: 37 | json.dump(classes, f) 38 | 39 | with open('professors.json', 'w') as f: 40 | f.write('var professors = ') 41 | json.dump(list(professors), f) 42 | f.write(';') 43 | 44 | if __name__ == '__main__': 45 | getcontext().prec = 2 46 | main() 47 | -------------------------------------------------------------------------------- /update.py: -------------------------------------------------------------------------------- 1 | from datetime import datetime, timedelta 2 | import os 3 | import shutil 4 | import subprocess 5 | 6 | OLD_TERM = "2022SP" 7 | NEW_TERM = "2023FA" 8 | 9 | # this are inclusive 10 | START_DATE = "2022-09-07" 11 | HALF_1_END_DATE = "2022-10-28" 12 | HALF_2_START_DATE = "2022-10-31" 13 | END_DATE = "2022-12-14" 14 | MONDAY_SCHEDULE = "2022-09-12" 15 | HOLIDAYS = [ 16 | "2022-09-23", 17 | "2022-10-10", 18 | "2022-10-11", 19 | "2022-11-11", 20 | "2022-11-24", 21 | "2022-11-25", 22 | ] 23 | 24 | 25 | def compute_dates(start, half_1_end, half_2_start, end, monday, holidays): 26 | DELTA_DAY = timedelta(days=1) 27 | start_dates = [""] * 5 28 | half_1_end_dates = [""] * 5 29 | half_2_start_dates = [""] * 5 30 | end_dates = [""] * 5 31 | r_dates = [""] * 5 32 | # ex_dates cannot be empty, so add a random date 33 | ex_dates = [["20000101"] for _ in range(5)] 34 | 35 | # fill start_dates, r_dates 36 | start = datetime.fromisoformat(start) 37 | while not all(d for d in start_dates): 38 | weekday = start.weekday() 39 | if 0 <= weekday <= 4: 40 | # yes, this is correct 41 | start_dates[weekday] = start.strftime("%Y-%m-%d") 42 | r_dates[weekday] = start.strftime("%Y%m%d") 43 | start += DELTA_DAY 44 | 45 | # fill half 1 end dates; move up one for inclusivity issues 46 | half_1_end = datetime.fromisoformat(half_1_end) + DELTA_DAY 47 | while not all(d for d in half_1_end_dates): 48 | weekday = half_1_end.weekday() 49 | if 1 <= weekday <= 5: 50 | half_1_end_dates[weekday - 1] = half_1_end.strftime("%Y%m%d") 51 | half_1_end -= DELTA_DAY 52 | 53 | # fill half 2 start dates 54 | half_2_start = datetime.fromisoformat(half_2_start) 55 | while not all(d for d in half_2_start_dates): 56 | weekday = half_2_start.weekday() 57 | if 0 <= weekday <= 4: 58 | half_2_start_dates[weekday] = half_2_start.strftime("%Y-%m-%d") 59 | half_2_start += DELTA_DAY 60 | 61 | # due to inclusivity issues, we actually move the end_dates up one 62 | end = datetime.fromisoformat(end) + DELTA_DAY 63 | while not all(d for d in end_dates): 64 | weekday = end.weekday() 65 | if 1 <= weekday <= 5: 66 | end_dates[weekday - 1] = end.strftime("%Y%m%d") 67 | end -= DELTA_DAY 68 | 69 | # handle holidays 70 | if monday: 71 | # the only possibility is that a tuesday becomes a monday 72 | date = datetime.fromisoformat(monday) 73 | r_dates[1] = date.strftime("%Y%m%d") 74 | for d in holidays: 75 | date = datetime.fromisoformat(d) 76 | ex_dates[date.weekday()].append(date.strftime("%Y%m%d")) 77 | 78 | return ( 79 | start_dates, 80 | half_1_end_dates, 81 | half_2_start_dates, 82 | end_dates, 83 | r_dates, 84 | ex_dates, 85 | ) 86 | 87 | 88 | class Term: 89 | def __init__(self, name): 90 | self.name = name 91 | 92 | self.sem = name[4].lower() 93 | if self.sem == "f": 94 | self.sem_full = "fall" 95 | self.sem_full_caps = "Fall" 96 | elif self.sem == "s": 97 | self.sem_full = "spring" 98 | self.sem_full_caps = "Spring" 99 | elif self.sem == "j": 100 | self.sem = "i" 101 | self.sem_full = "iap" 102 | self.sem_full_caps = "IAP" 103 | 104 | self.name_year = name[:4] 105 | self.actual_year = self.name_year 106 | if self.sem == "f": 107 | self.actual_year = str(int(self.name_year) - 1) 108 | 109 | self.sem_year = f"{self.sem}{self.actual_year[-2:]}" 110 | self.sem_full_year = f"{self.sem_full}{self.actual_year[-2:]}" 111 | self.full_name = f"{self.sem_full_caps} {self.actual_year}" 112 | 113 | 114 | old_term = Term(OLD_TERM) 115 | new_term = Term(NEW_TERM) 116 | 117 | # make directory 118 | www_dir = f"./www/" 119 | # e.g. www/semesters/f21 120 | old_term_dir = f"./www/semesters/{old_term.sem_year}/" 121 | old_term_file = old_term_dir + f"{old_term.sem_full}.html" 122 | os.makedirs(old_term_dir, exist_ok=True) 123 | 124 | # copy files, rename script.js -> e.g. fall.js 125 | shutil.copy(www_dir + "index.html", old_term_file) 126 | shutil.copy(www_dir + "script-compiled.js", old_term_dir + "script-compiled.js") 127 | shutil.copy(www_dir + "full.js", old_term_dir + f"{old_term.sem_full}.js") 128 | 129 | with open(old_term_file, "r") as file: 130 | lines = file.readlines() 131 | 132 | # in old_term_file, replace src=" to src="../../ 133 | # exception: should not start with src="http 134 | # exception: should not be src="full.js", 135 | # in this case, replace with "fall/spring.js" 136 | # exception: should not be src="script-compiled.js" 137 | new_lines = [] 138 | for line in lines: 139 | if not any( 140 | s in line for s in ['src="http', 'src="full.js"', 'src="script-compiled.js"'] 141 | ): 142 | line = line.replace('src="', 'src="../../') 143 | if 'src="full.js"' in line: 144 | line = line.replace("full.js", f"{old_term.sem_full}.js") 145 | new_lines.append(line) 146 | lines = new_lines[:] 147 | 148 | new_lines = [] 149 | # in old_term_file, replace href=" to href="../../ 150 | # exception: should not start with href="http or href="mailto or href="data 151 | for line in lines: 152 | if not any(s in line for s in ['href="http', 'href="mailto', 'href="data']): 153 | line = line.replace('href="', 'href="../../') 154 | new_lines.append(line) 155 | lines = new_lines[:] 156 | 157 | # in old_term_file, remove line with value="index.html" and add the (indented) line: 158 | new_option = f'{" "*12}\n' 159 | # add "selected" option to this line 160 | new_index = f'{" "*12}\n' 161 | # add ^ above this line 162 | # replace value="semesters with value=".. 163 | 164 | 165 | def update_dropdown(lines, replace_selected=False): 166 | new_lines = [] 167 | flag = "not seen" 168 | for line in lines: 169 | if flag == "not seen": 170 | new_lines.append(line) 171 | if 'select name="semesters"' in line: 172 | flag = "seen" 173 | elif flag == "seen": 174 | # it's already been added i guess 175 | if new_term.full_name in line: 176 | new_lines.append(line) 177 | flag = "done" 178 | continue 179 | # otherwise, ignore this line 180 | new_lines.append(new_index) 181 | if replace_selected: 182 | new_lines.append(new_option.replace('">', '" selected>')) 183 | else: 184 | new_lines.append(new_option) 185 | flag = "done" 186 | elif flag == "done": 187 | new_lines.append(line.replace('value="semesters', 'value="..')) 188 | return new_lines 189 | 190 | 191 | with open(old_term_file, "w") as file: 192 | file.writelines(update_dropdown(lines, True)) 193 | 194 | # in all files in old semesters, find the line with value="../../index.html" 195 | # replace with the new index line, then add the line for e.g. Fall 2021 196 | for folder in os.scandir("./www/semesters"): 197 | if not folder.is_dir(): 198 | continue 199 | if old_term.sem_year in folder.path: 200 | continue 201 | for path in os.scandir(folder): 202 | if not path.name.endswith(".html"): 203 | continue 204 | with open(path, "r") as file: 205 | lines = file.readlines() 206 | with open(path, "w") as file: 207 | file.writelines(update_dropdown(lines)) 208 | 209 | # update new_scripts/coursews.py term 210 | coursews_path = "./new_scripts/coursews.py" 211 | with open(coursews_path, "r") as file: 212 | lines = file.readlines() 213 | 214 | new_lines = [] 215 | for line in lines: 216 | if line.startswith("term ="): 217 | new_lines.append(f"term = '{new_term.name}'\n") 218 | else: 219 | new_lines.append(line) 220 | 221 | with open(coursews_path, "w") as file: 222 | file.writelines(new_lines) 223 | 224 | # run normal update process 225 | # something something some classes need special casing 226 | print("this might not work, if it fails run new_scripts/update_schedule.sh manually:") 227 | subprocess.run("./new_scripts/update_schedule.sh", shell=True) 228 | 229 | # in (new) index.html: 230 | new_term_file = "./www/index.html" 231 | with open(new_term_file, "r") as file: 232 | lines = file.readlines() 233 | 234 | # update dropdown 235 | new_lines = [] 236 | flag = "not seen" 237 | for line in lines: 238 | if flag == "not seen": 239 | new_lines.append(line) 240 | if 'select name="semesters"' in line: 241 | flag = "seen" 242 | elif flag == "seen": 243 | # ignore this line 244 | new_lines.append( 245 | f'{" "*12}\n' 246 | ) 247 | new_lines.append( 248 | f'{" "*12}\n' 249 | ) 250 | flag = "done" 251 | elif flag == "done": 252 | new_lines.append(line) 253 | lines = new_lines[:] 254 | 255 | # fix comma placement 256 | new_lines = [] 257 | for line in lines: 258 | if 'title="Fall"' in line: 259 | if new_term.sem == "f": # remove comma 260 | line = line.replace("/>,<", "/><") 261 | elif new_term.sem == "s": # add comma 262 | line = line.replace("/><", "/>,<") 263 | elif 'id="spring-span"' in line: 264 | if new_term.sem == "f": # add comma 265 | line = line.replace("><", ">,<") 266 | elif new_term.sem == "s": # remove comma 267 | line = line.replace(">,<", "><") 268 | new_lines.append(line) 269 | lines = new_lines[:] 270 | 271 | # change Not offered 20xx-20yy in index.html 272 | new_lines = [] 273 | for line in lines: 274 | if "Not offered" in line: 275 | line = f' class="lazyload-img" data-toggle="tooltip" data-placement="top" title="Not offered {new_term.name_year}-{str(int(new_term.name_year) + 1)}" data-trigger="hover"/> spring21 287 | # replace Fall 2020 -> Spring 2021 288 | new_lines = [] 289 | for line in lines: 290 | new_lines.append( 291 | line.replace(old_term.sem_full_year, new_term.sem_full_year).replace( 292 | old_term.full_name, new_term.full_name 293 | ) 294 | ) 295 | lines = new_lines[:] 296 | 297 | # update the mit schedule for gcal export in script.js 298 | ( 299 | start_dates, 300 | half_1_end_dates, 301 | half_2_start_dates, 302 | end_dates, 303 | r_dates, 304 | ex_dates, 305 | ) = compute_dates( 306 | START_DATE, HALF_1_END_DATE, HALF_2_START_DATE, END_DATE, MONDAY_SCHEDULE, HOLIDAYS 307 | ) 308 | new_lines = [] 309 | indent = "\t" * 3 310 | for line in lines: 311 | if "var start_dates =" in line: 312 | line = f"{indent}var start_dates = {repr(start_dates)};\n" 313 | elif "var half_1_end_dates =" in line: 314 | line = f"{indent}var half_1_end_dates = {repr(half_1_end_dates)};\n" 315 | elif "var half_2_start_dates =" in line: 316 | line = f"{indent}var half_2_start_dates = {repr(half_2_start_dates)};\n" 317 | elif "var end_dates =" in line: 318 | line = f"{indent}var end_dates = {repr(end_dates)};\n" 319 | elif "var r_dates =" in line: 320 | line = f"{indent}var r_dates = {repr(r_dates)};\n" 321 | elif "var ex_dates =" in line: 322 | line = f"{indent}var ex_dates = {repr(ex_dates)};\n" 323 | new_lines.append(line) 324 | 325 | with open(script_js, "w") as file: 326 | file.writelines(new_lines) 327 | 328 | # recompile using new_scripts/compile.sh 329 | print("this might not work, if it fails run new_scripts/compile.sh manually:") 330 | subprocess.run("./new_scripts/compile.sh", shell=True) 331 | -------------------------------------------------------------------------------- /www/dataTables.scroller.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | Scroller 1.4.2 3 | ©2011-2016 SpryMedia Ltd - datatables.net/license 4 | */ 5 | (function(e){"function"===typeof define&&define.amd?define(["jquery","datatables.net"],function(h){return e(h,window,document)}):"object"===typeof exports?module.exports=function(h,j){h||(h=window);if(!j||!j.fn.dataTable)j=require("datatables.net")(h,j).$;return e(j,h,h.document)}:e(jQuery,window,document)})(function(e,h,j,l){var m=e.fn.dataTable,g=function(a,b){this instanceof g?(b===l&&(b={}),this.s={dt:e.fn.dataTable.Api(a).settings()[0],tableTop:0,tableBottom:0,redrawTop:0,redrawBottom:0,autoHeight:!0, 6 | viewportRows:0,stateTO:null,drawTO:null,heights:{jump:null,page:null,virtual:null,scroll:null,row:null,viewport:null},topRowFloat:0,scrollDrawDiff:null,loaderVisible:!1},this.s=e.extend(this.s,g.oDefaults,b),this.s.heights.row=this.s.rowHeight,this.dom={force:j.createElement("div"),scroller:null,table:null,loader:null},this.s.dt.oScroller||(this.s.dt.oScroller=this,this._fnConstruct())):alert("Scroller warning: Scroller must be initialised with the 'new' keyword.")};e.extend(g.prototype,{fnRowToPixels:function(a, 7 | b,c){a=c?this._domain("virtualToPhysical",a*this.s.heights.row):this.s.baseScrollTop+(a-this.s.baseRowTop)*this.s.heights.row;return b||b===l?parseInt(a,10):a},fnPixelsToRow:function(a,b,c){var d=a-this.s.baseScrollTop,a=c?this._domain("physicalToVirtual",a)/this.s.heights.row:d/this.s.heights.row+this.s.baseRowTop;return b||b===l?parseInt(a,10):a},fnScrollToRow:function(a,b){var c=this,d=!1,f=this.fnRowToPixels(a),i=a-(this.s.displayBuffer-1)/2*this.s.viewportRows;0>i&&(i=0);if((f>this.s.redrawBottom|| 8 | ftable",this.dom.scroller)[0];this.dom.table.style.position="absolute";this.dom.table.style.top="0px";this.dom.table.style.left="0px";e(this.s.dt.nTableWrapper).addClass("DTS");this.s.loadingIndicator&&(this.dom.loader=e('
'+this.s.dt.oLanguage.sLoadingRecords+"
").css("display", 11 | "none"),e(this.dom.scroller.parentNode).css("position","relative").append(this.dom.loader));this.s.heights.row&&"auto"!=this.s.heights.row&&(this.s.autoHeight=!1);this.fnMeasure(!1);this.s.ingnoreScroll=!0;this.s.stateSaveThrottle=this.s.dt.oApi._fnThrottle(function(){a.s.dt.oApi._fnSaveState(a.s.dt)},500);e(this.dom.scroller).on("scroll.DTS",function(){a._fnScroll.call(a)});e(this.dom.scroller).on("touchstart.DTS",function(){a._fnScroll.call(a)});this.s.dt.aoDrawCallback.push({fn:function(){a.s.dt.bInitialised&& 12 | a._fnDrawCallback.call(a)},sName:"Scroller"});e(h).on("resize.DTS",function(){a.fnMeasure(false);a._fnInfo()});var b=!0;this.s.dt.oApi._fnCallbackReg(this.s.dt,"aoStateSaveParams",function(c,d){if(b&&a.s.dt.oLoadedState){d.iScroller=a.s.dt.oLoadedState.iScroller;d.iScrollerTopRow=a.s.dt.oLoadedState.iScrollerTopRow;b=false}else{d.iScroller=a.dom.scroller.scrollTop;d.iScrollerTopRow=a.s.topRowFloat}},"Scroller_State");this.s.dt.oLoadedState&&(this.s.topRowFloat=this.s.dt.oLoadedState.iScrollerTopRow|| 13 | 0);e(this.s.dt.nTable).one("init.dt",function(){a.fnMeasure()});this.s.dt.aoDestroyCallback.push({sName:"Scroller",fn:function(){e(h).off("resize.DTS");e(a.dom.scroller).off("touchstart.DTS scroll.DTS");e(a.s.dt.nTableWrapper).removeClass("DTS");e("div.DTS_Loading",a.dom.scroller.parentNode).remove();e(a.s.dt.nTable).off("init.dt");a.dom.table.style.position="";a.dom.table.style.top="";a.dom.table.style.left=""}})}else this.s.dt.oApi._fnLog(this.s.dt,0,"Pagination must be enabled for Scroller")}, 14 | _fnScroll:function(){var a=this,b=this.s.heights,c=this.dom.scroller.scrollTop,d;if(!this.s.skip&&!this.s.ingnoreScroll)if(this.s.dt.bFiltered||this.s.dt.bSorted)this.s.lastScrollTop=0;else{this._fnInfo();clearTimeout(this.s.stateTO);this.s.stateTO=setTimeout(function(){a.s.dt.oApi._fnSaveState(a.s.dt)},250);if(cthis.s.redrawBottom){var f=Math.ceil((this.s.displayBuffer-1)/2*this.s.viewportRows);Math.abs(c-this.s.lastScrollTop)>b.viewport||this.s.ani?(d=parseInt(this._domain("physicalToVirtual", 15 | c)/b.row,10)-f,this.s.topRowFloat=this._domain("physicalToVirtual",c)/b.row):(d=this.fnPixelsToRow(c)-f,this.s.topRowFloat=this.fnPixelsToRow(c,!1));0>=d?d=0:d+this.s.dt._iDisplayLength>this.s.dt.fnRecordsDisplay()?(d=this.s.dt.fnRecordsDisplay()-this.s.dt._iDisplayLength,0>d&&(d=0)):0!==d%2&&d++;if(d!=this.s.dt._iDisplayStart&&(this.s.tableTop=e(this.s.dt.nTable).offset().top,this.s.tableBottom=e(this.s.dt.nTable).height()+this.s.tableTop,b=function(){if(a.s.scrollDrawReq===null)a.s.scrollDrawReq= 16 | c;a.s.dt._iDisplayStart=d;a.s.dt.oApi._fnDraw(a.s.dt)},this.s.dt.oFeatures.bServerSide?(clearTimeout(this.s.drawTO),this.s.drawTO=setTimeout(b,this.s.serverWait)):b(),this.dom.loader&&!this.s.loaderVisible))this.dom.loader.css("display","block"),this.s.loaderVisible=!0}else this.s.topRowFloat=this._domain("physicalToVirtual",c)/b.row;this.s.lastScrollTop=c;this.s.stateSaveThrottle()}},_domain:function(a,b){var c=this.s.heights,d;if(c.virtual===c.scroll)return b;var e=(c.scroll-c.viewport)/2,i=(c.virtual- 17 | c.viewport)/2;d=i/(e*e);if("virtualToPhysical"===a){if(bb?c.scroll:2*e-Math.pow(b/d,0.5)}if("physicalToVirtual"===a){if(bb?c.virtual:2*i-b*b*d}},_fnDrawCallback:function(){var a=this,b=this.s.heights,c=this.dom.scroller.scrollTop,d=e(this.s.dt.nTable).height(),f=this.s.dt._iDisplayStart,i=this.s.dt._iDisplayLength,g=this.s.dt.fnRecordsDisplay();this.s.skip=!0;this._fnScrollForce();c=0===f?this.s.topRowFloat*b.row:f+i>=g? 18 | b.scroll-(g-this.s.topRowFloat)*b.row:this._domain("virtualToPhysical",this.s.topRowFloat*b.row);this.dom.scroller.scrollTop=c;this.s.baseScrollTop=c;this.s.baseRowTop=this.s.topRowFloat;var h=c-(this.s.topRowFloat-f)*b.row;0===f?h=0:f+i>=g&&(h=b.scroll-d);this.dom.table.style.top=h+"px";this.s.tableTop=h;this.s.tableBottom=d+this.s.tableTop;d=(c-this.s.tableTop)*this.s.boundaryScale;this.s.redrawTop=c-d;this.s.redrawBottom=c+d;this.s.skip=!1;this.s.dt.oFeatures.bStateSave&&null!==this.s.dt.oLoadedState&& 19 | "undefined"!=typeof this.s.dt.oLoadedState.iScroller?((c=(this.s.dt.sAjaxSource||a.s.dt.ajax)&&!this.s.dt.oFeatures.bServerSide?!0:!1)&&2==this.s.dt.iDraw||!c&&1==this.s.dt.iDraw)&&setTimeout(function(){e(a.dom.scroller).scrollTop(a.s.dt.oLoadedState.iScroller);a.s.redrawTop=a.s.dt.oLoadedState.iScroller-b.viewport/2;setTimeout(function(){a.s.ingnoreScroll=!1},0)},0):a.s.ingnoreScroll=!1;this.s.dt.oFeatures.bInfo&&setTimeout(function(){a._fnInfo.call(a)},0);this.dom.loader&&this.s.loaderVisible&& 20 | (this.dom.loader.css("display","none"),this.s.loaderVisible=!1)},_fnScrollForce:function(){var a=this.s.heights;a.virtual=a.row*this.s.dt.fnRecordsDisplay();a.scroll=a.virtual;1E6this.s.heights.row?a.scroll+"px":this.s.heights.row+"px"},_fnCalcRowHeight:function(){var a=this.s.dt,b=a.nTable,c=b.cloneNode(!1),d=e("").appendTo(c),f=e('
');for(e("tbody tr:lt(4)",b).clone().appendTo(d);3>e("tr",d).length;)d.append(" ");e("div."+a.oClasses.sScrollBody,f).append(c);a=this.s.dt.nHolding||b.parentNode;e(a).is(":visible")||(a="body");f.appendTo(a);this.s.heights.row=e("tr",d).eq(1).outerHeight();f.remove()},_fnInfo:function(){if(this.s.dt.oFeatures.bInfo){var a=this.s.dt,b=a.oLanguage,c=this.dom.scroller.scrollTop,d=Math.floor(this.fnPixelsToRow(c,!1,this.s.ani)+1),f=a.fnRecordsTotal(), 22 | i=a.fnRecordsDisplay(),c=Math.ceil(this.fnPixelsToRow(c+this.s.heights.viewport,!1,this.s.ani)),c=i 2 | 3 | btn_google_signin_light_normal_web 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | Export to Google Calendar 33 | 34 | 35 | -------------------------------------------------------------------------------- /www/img/cih1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/edfan/firehose/daaba0d1eef8a9d363cc39efc17e9df6ff96b610/www/img/cih1.gif -------------------------------------------------------------------------------- /www/img/cihw.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/edfan/firehose/daaba0d1eef8a9d363cc39efc17e9df6ff96b610/www/img/cihw.gif -------------------------------------------------------------------------------- /www/img/cstimathon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/edfan/firehose/daaba0d1eef8a9d363cc39efc17e9df6ff96b610/www/img/cstimathon.png -------------------------------------------------------------------------------- /www/img/fall.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/edfan/firehose/daaba0d1eef8a9d363cc39efc17e9df6ff96b610/www/img/fall.gif -------------------------------------------------------------------------------- /www/img/fall_new.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/edfan/firehose/daaba0d1eef8a9d363cc39efc17e9df6ff96b610/www/img/fall_new.gif -------------------------------------------------------------------------------- /www/img/fall_new.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/edfan/firehose/daaba0d1eef8a9d363cc39efc17e9df6ff96b610/www/img/fall_new.psd -------------------------------------------------------------------------------- /www/img/grad.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/edfan/firehose/daaba0d1eef8a9d363cc39efc17e9df6ff96b610/www/img/grad.gif -------------------------------------------------------------------------------- /www/img/hassA.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/edfan/firehose/daaba0d1eef8a9d363cc39efc17e9df6ff96b610/www/img/hassA.gif -------------------------------------------------------------------------------- /www/img/hassE.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/edfan/firehose/daaba0d1eef8a9d363cc39efc17e9df6ff96b610/www/img/hassE.gif -------------------------------------------------------------------------------- /www/img/hassH.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/edfan/firehose/daaba0d1eef8a9d363cc39efc17e9df6ff96b610/www/img/hassH.gif -------------------------------------------------------------------------------- /www/img/hassS.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/edfan/firehose/daaba0d1eef8a9d363cc39efc17e9df6ff96b610/www/img/hassS.gif -------------------------------------------------------------------------------- /www/img/iap.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/edfan/firehose/daaba0d1eef8a9d363cc39efc17e9df6ff96b610/www/img/iap.gif -------------------------------------------------------------------------------- /www/img/js-large.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/edfan/firehose/daaba0d1eef8a9d363cc39efc17e9df6ff96b610/www/img/js-large.jpeg -------------------------------------------------------------------------------- /www/img/js-small.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/edfan/firehose/daaba0d1eef8a9d363cc39efc17e9df6ff96b610/www/img/js-small.jpeg -------------------------------------------------------------------------------- /www/img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/edfan/firehose/daaba0d1eef8a9d363cc39efc17e9df6ff96b610/www/img/logo.png -------------------------------------------------------------------------------- /www/img/logo_pride.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/edfan/firehose/daaba0d1eef8a9d363cc39efc17e9df6ff96b610/www/img/logo_pride.png -------------------------------------------------------------------------------- /www/img/nonext.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/edfan/firehose/daaba0d1eef8a9d363cc39efc17e9df6ff96b610/www/img/nonext.gif -------------------------------------------------------------------------------- /www/img/projx.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/edfan/firehose/daaba0d1eef8a9d363cc39efc17e9df6ff96b610/www/img/projx.png -------------------------------------------------------------------------------- /www/img/repeat.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/edfan/firehose/daaba0d1eef8a9d363cc39efc17e9df6ff96b610/www/img/repeat.gif -------------------------------------------------------------------------------- /www/img/rest.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/edfan/firehose/daaba0d1eef8a9d363cc39efc17e9df6ff96b610/www/img/rest.gif -------------------------------------------------------------------------------- /www/img/spring.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/edfan/firehose/daaba0d1eef8a9d363cc39efc17e9df6ff96b610/www/img/spring.gif -------------------------------------------------------------------------------- /www/img/spring_new.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/edfan/firehose/daaba0d1eef8a9d363cc39efc17e9df6ff96b610/www/img/spring_new.gif -------------------------------------------------------------------------------- /www/img/spring_new.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/edfan/firehose/daaba0d1eef8a9d363cc39efc17e9df6ff96b610/www/img/spring_new.psd -------------------------------------------------------------------------------- /www/img/summer.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/edfan/firehose/daaba0d1eef8a9d363cc39efc17e9df6ff96b610/www/img/summer.gif -------------------------------------------------------------------------------- /www/img/under.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/edfan/firehose/daaba0d1eef8a9d363cc39efc17e9df6ff96b610/www/img/under.gif -------------------------------------------------------------------------------- /www/jquery-ui.min.css: -------------------------------------------------------------------------------- 1 | /*! jQuery UI - v1.12.1 - 2017-07-01 2 | * http://jqueryui.com 3 | * Includes: core.css, slider.css, theme.css 4 | * To view and modify this theme, visit http://jqueryui.com/themeroller/?scope=&folderName=base&cornerRadiusShadow=8px&offsetLeftShadow=0px&offsetTopShadow=0px&thicknessShadow=5px&opacityShadow=30&bgImgOpacityShadow=0&bgTextureShadow=flat&bgColorShadow=666666&opacityOverlay=30&bgImgOpacityOverlay=0&bgTextureOverlay=flat&bgColorOverlay=aaaaaa&iconColorError=cc0000&fcError=5f3f3f&borderColorError=f1a899&bgTextureError=flat&bgColorError=fddfdf&iconColorHighlight=777620&fcHighlight=777620&borderColorHighlight=dad55e&bgTextureHighlight=flat&bgColorHighlight=fffa90&iconColorActive=ffffff&fcActive=ffffff&borderColorActive=003eff&bgTextureActive=flat&bgColorActive=007fff&iconColorHover=555555&fcHover=2b2b2b&borderColorHover=cccccc&bgTextureHover=flat&bgColorHover=ededed&iconColorDefault=777777&fcDefault=454545&borderColorDefault=c5c5c5&bgTextureDefault=flat&bgColorDefault=f6f6f6&iconColorContent=444444&fcContent=333333&borderColorContent=dddddd&bgTextureContent=flat&bgColorContent=ffffff&iconColorHeader=444444&fcHeader=333333&borderColorHeader=dddddd&bgTextureHeader=flat&bgColorHeader=e9e9e9&cornerRadius=3px&fwDefault=normal&fsDefault=1em&ffDefault=Arial%2CHelvetica%2Csans-serif 5 | * Copyright jQuery Foundation and other contributors; Licensed MIT */ 6 | 7 | .ui-helper-hidden{display:none}.ui-helper-hidden-accessible{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.ui-helper-reset{margin:0;padding:0;border:0;outline:0;line-height:1.3;text-decoration:none;font-size:100%;list-style:none}.ui-helper-clearfix:before,.ui-helper-clearfix:after{content:"";display:table;border-collapse:collapse}.ui-helper-clearfix:after{clear:both}.ui-helper-zfix{width:100%;height:100%;top:0;left:0;position:absolute;opacity:0;filter:Alpha(Opacity=0)}.ui-front{z-index:100}.ui-state-disabled{cursor:default!important;pointer-events:none}.ui-icon{display:inline-block;vertical-align:middle;margin-top:-.25em;position:relative;text-indent:-99999px;overflow:hidden;background-repeat:no-repeat}.ui-widget-icon-block{left:50%;margin-left:-8px;display:block}.ui-widget-overlay{position:fixed;top:0;left:0;width:100%;height:100%}.ui-slider{position:relative;text-align:left}.ui-slider .ui-slider-handle{position:absolute;z-index:2;width:1.2em;height:1.2em;cursor:default;-ms-touch-action:none;touch-action:none}.ui-slider .ui-slider-range{position:absolute;z-index:1;font-size:.7em;display:block;border:0;background-position:0 0}.ui-slider.ui-state-disabled .ui-slider-handle,.ui-slider.ui-state-disabled .ui-slider-range{filter:inherit}.ui-slider-horizontal{height:.8em}.ui-slider-horizontal .ui-slider-handle{top:-.3em;margin-left:-.6em}.ui-slider-horizontal .ui-slider-range{top:0;height:100%}.ui-slider-horizontal .ui-slider-range-min{left:0}.ui-slider-horizontal .ui-slider-range-max{right:0}.ui-slider-vertical{width:.8em;height:100px}.ui-slider-vertical .ui-slider-handle{left:-.3em;margin-left:0;margin-bottom:-.6em}.ui-slider-vertical .ui-slider-range{left:0;width:100%}.ui-slider-vertical .ui-slider-range-min{bottom:0}.ui-slider-vertical .ui-slider-range-max{top:0}.ui-widget{font-family:Arial,Helvetica,sans-serif;font-size:1em}.ui-widget .ui-widget{font-size:1em}.ui-widget input,.ui-widget select,.ui-widget textarea,.ui-widget button{font-family:Arial,Helvetica,sans-serif;font-size:1em}.ui-widget.ui-widget-content{border:1px solid #c5c5c5}.ui-widget-content{border:1px solid #ddd;background:#fff;color:#333}.ui-widget-content a{color:#333}.ui-widget-header{border:1px solid #ddd;background:#e9e9e9;color:#333;font-weight:bold}.ui-widget-header a{color:#333}.ui-state-default,.ui-widget-content .ui-state-default,.ui-widget-header .ui-state-default,.ui-button,html .ui-button.ui-state-disabled:hover,html .ui-button.ui-state-disabled:active{border:1px solid #c5c5c5;background:#f6f6f6;font-weight:normal;color:#454545}.ui-state-default a,.ui-state-default a:link,.ui-state-default a:visited,a.ui-button,a:link.ui-button,a:visited.ui-button,.ui-button{color:#454545;text-decoration:none}.ui-state-hover,.ui-widget-content .ui-state-hover,.ui-widget-header .ui-state-hover,.ui-state-focus,.ui-widget-content .ui-state-focus,.ui-widget-header .ui-state-focus,.ui-button:hover,.ui-button:focus{border:1px solid #ccc;background:#ededed;font-weight:normal;color:#2b2b2b}.ui-state-hover a,.ui-state-hover a:hover,.ui-state-hover a:link,.ui-state-hover a:visited,.ui-state-focus a,.ui-state-focus a:hover,.ui-state-focus a:link,.ui-state-focus a:visited,a.ui-button:hover,a.ui-button:focus{color:#2b2b2b;text-decoration:none}.ui-visual-focus{box-shadow:0 0 3px 1px rgb(94,158,214)}.ui-state-active,.ui-widget-content .ui-state-active,.ui-widget-header .ui-state-active,a.ui-button:active,.ui-button:active,.ui-button.ui-state-active:hover{border:1px solid #003eff;background:#007fff;font-weight:normal;color:#fff}.ui-icon-background,.ui-state-active .ui-icon-background{border:#003eff;background-color:#fff}.ui-state-active a,.ui-state-active a:link,.ui-state-active a:visited{color:#fff;text-decoration:none}.ui-state-highlight,.ui-widget-content .ui-state-highlight,.ui-widget-header .ui-state-highlight{border:1px solid #dad55e;background:#fffa90;color:#777620}.ui-state-checked{border:1px solid #dad55e;background:#fffa90}.ui-state-highlight a,.ui-widget-content .ui-state-highlight a,.ui-widget-header .ui-state-highlight a{color:#777620}.ui-state-error,.ui-widget-content .ui-state-error,.ui-widget-header .ui-state-error{border:1px solid #f1a899;background:#fddfdf;color:#5f3f3f}.ui-state-error a,.ui-widget-content .ui-state-error a,.ui-widget-header .ui-state-error a{color:#5f3f3f}.ui-state-error-text,.ui-widget-content .ui-state-error-text,.ui-widget-header .ui-state-error-text{color:#5f3f3f}.ui-priority-primary,.ui-widget-content .ui-priority-primary,.ui-widget-header .ui-priority-primary{font-weight:bold}.ui-priority-secondary,.ui-widget-content .ui-priority-secondary,.ui-widget-header .ui-priority-secondary{opacity:.7;filter:Alpha(Opacity=70);font-weight:normal}.ui-state-disabled,.ui-widget-content .ui-state-disabled,.ui-widget-header .ui-state-disabled{opacity:.35;filter:Alpha(Opacity=35);background-image:none}.ui-state-disabled .ui-icon{filter:Alpha(Opacity=35)}.ui-icon{width:16px;height:16px}.ui-icon,.ui-widget-content .ui-icon{background-image:url("images/ui-icons_444444_256x240.png")}.ui-widget-header .ui-icon{background-image:url("images/ui-icons_444444_256x240.png")}.ui-state-hover .ui-icon,.ui-state-focus .ui-icon,.ui-button:hover .ui-icon,.ui-button:focus .ui-icon{background-image:url("images/ui-icons_555555_256x240.png")}.ui-state-active .ui-icon,.ui-button:active .ui-icon{background-image:url("images/ui-icons_ffffff_256x240.png")}.ui-state-highlight .ui-icon,.ui-button .ui-state-highlight.ui-icon{background-image:url("images/ui-icons_777620_256x240.png")}.ui-state-error .ui-icon,.ui-state-error-text .ui-icon{background-image:url("images/ui-icons_cc0000_256x240.png")}.ui-button .ui-icon{background-image:url("images/ui-icons_777777_256x240.png")}.ui-icon-blank{background-position:16px 16px}.ui-icon-caret-1-n{background-position:0 0}.ui-icon-caret-1-ne{background-position:-16px 0}.ui-icon-caret-1-e{background-position:-32px 0}.ui-icon-caret-1-se{background-position:-48px 0}.ui-icon-caret-1-s{background-position:-65px 0}.ui-icon-caret-1-sw{background-position:-80px 0}.ui-icon-caret-1-w{background-position:-96px 0}.ui-icon-caret-1-nw{background-position:-112px 0}.ui-icon-caret-2-n-s{background-position:-128px 0}.ui-icon-caret-2-e-w{background-position:-144px 0}.ui-icon-triangle-1-n{background-position:0 -16px}.ui-icon-triangle-1-ne{background-position:-16px -16px}.ui-icon-triangle-1-e{background-position:-32px -16px}.ui-icon-triangle-1-se{background-position:-48px -16px}.ui-icon-triangle-1-s{background-position:-65px -16px}.ui-icon-triangle-1-sw{background-position:-80px -16px}.ui-icon-triangle-1-w{background-position:-96px -16px}.ui-icon-triangle-1-nw{background-position:-112px -16px}.ui-icon-triangle-2-n-s{background-position:-128px -16px}.ui-icon-triangle-2-e-w{background-position:-144px -16px}.ui-icon-arrow-1-n{background-position:0 -32px}.ui-icon-arrow-1-ne{background-position:-16px -32px}.ui-icon-arrow-1-e{background-position:-32px -32px}.ui-icon-arrow-1-se{background-position:-48px -32px}.ui-icon-arrow-1-s{background-position:-65px -32px}.ui-icon-arrow-1-sw{background-position:-80px -32px}.ui-icon-arrow-1-w{background-position:-96px -32px}.ui-icon-arrow-1-nw{background-position:-112px -32px}.ui-icon-arrow-2-n-s{background-position:-128px -32px}.ui-icon-arrow-2-ne-sw{background-position:-144px -32px}.ui-icon-arrow-2-e-w{background-position:-160px -32px}.ui-icon-arrow-2-se-nw{background-position:-176px -32px}.ui-icon-arrowstop-1-n{background-position:-192px -32px}.ui-icon-arrowstop-1-e{background-position:-208px -32px}.ui-icon-arrowstop-1-s{background-position:-224px -32px}.ui-icon-arrowstop-1-w{background-position:-240px -32px}.ui-icon-arrowthick-1-n{background-position:1px -48px}.ui-icon-arrowthick-1-ne{background-position:-16px -48px}.ui-icon-arrowthick-1-e{background-position:-32px -48px}.ui-icon-arrowthick-1-se{background-position:-48px -48px}.ui-icon-arrowthick-1-s{background-position:-64px -48px}.ui-icon-arrowthick-1-sw{background-position:-80px -48px}.ui-icon-arrowthick-1-w{background-position:-96px -48px}.ui-icon-arrowthick-1-nw{background-position:-112px -48px}.ui-icon-arrowthick-2-n-s{background-position:-128px -48px}.ui-icon-arrowthick-2-ne-sw{background-position:-144px -48px}.ui-icon-arrowthick-2-e-w{background-position:-160px -48px}.ui-icon-arrowthick-2-se-nw{background-position:-176px -48px}.ui-icon-arrowthickstop-1-n{background-position:-192px -48px}.ui-icon-arrowthickstop-1-e{background-position:-208px -48px}.ui-icon-arrowthickstop-1-s{background-position:-224px -48px}.ui-icon-arrowthickstop-1-w{background-position:-240px -48px}.ui-icon-arrowreturnthick-1-w{background-position:0 -64px}.ui-icon-arrowreturnthick-1-n{background-position:-16px -64px}.ui-icon-arrowreturnthick-1-e{background-position:-32px -64px}.ui-icon-arrowreturnthick-1-s{background-position:-48px -64px}.ui-icon-arrowreturn-1-w{background-position:-64px -64px}.ui-icon-arrowreturn-1-n{background-position:-80px -64px}.ui-icon-arrowreturn-1-e{background-position:-96px -64px}.ui-icon-arrowreturn-1-s{background-position:-112px -64px}.ui-icon-arrowrefresh-1-w{background-position:-128px -64px}.ui-icon-arrowrefresh-1-n{background-position:-144px -64px}.ui-icon-arrowrefresh-1-e{background-position:-160px -64px}.ui-icon-arrowrefresh-1-s{background-position:-176px -64px}.ui-icon-arrow-4{background-position:0 -80px}.ui-icon-arrow-4-diag{background-position:-16px -80px}.ui-icon-extlink{background-position:-32px -80px}.ui-icon-newwin{background-position:-48px -80px}.ui-icon-refresh{background-position:-64px -80px}.ui-icon-shuffle{background-position:-80px -80px}.ui-icon-transfer-e-w{background-position:-96px -80px}.ui-icon-transferthick-e-w{background-position:-112px -80px}.ui-icon-folder-collapsed{background-position:0 -96px}.ui-icon-folder-open{background-position:-16px -96px}.ui-icon-document{background-position:-32px -96px}.ui-icon-document-b{background-position:-48px -96px}.ui-icon-note{background-position:-64px -96px}.ui-icon-mail-closed{background-position:-80px -96px}.ui-icon-mail-open{background-position:-96px -96px}.ui-icon-suitcase{background-position:-112px -96px}.ui-icon-comment{background-position:-128px -96px}.ui-icon-person{background-position:-144px -96px}.ui-icon-print{background-position:-160px -96px}.ui-icon-trash{background-position:-176px -96px}.ui-icon-locked{background-position:-192px -96px}.ui-icon-unlocked{background-position:-208px -96px}.ui-icon-bookmark{background-position:-224px -96px}.ui-icon-tag{background-position:-240px -96px}.ui-icon-home{background-position:0 -112px}.ui-icon-flag{background-position:-16px -112px}.ui-icon-calendar{background-position:-32px -112px}.ui-icon-cart{background-position:-48px -112px}.ui-icon-pencil{background-position:-64px -112px}.ui-icon-clock{background-position:-80px -112px}.ui-icon-disk{background-position:-96px -112px}.ui-icon-calculator{background-position:-112px -112px}.ui-icon-zoomin{background-position:-128px -112px}.ui-icon-zoomout{background-position:-144px -112px}.ui-icon-search{background-position:-160px -112px}.ui-icon-wrench{background-position:-176px -112px}.ui-icon-gear{background-position:-192px -112px}.ui-icon-heart{background-position:-208px -112px}.ui-icon-star{background-position:-224px -112px}.ui-icon-link{background-position:-240px -112px}.ui-icon-cancel{background-position:0 -128px}.ui-icon-plus{background-position:-16px -128px}.ui-icon-plusthick{background-position:-32px -128px}.ui-icon-minus{background-position:-48px -128px}.ui-icon-minusthick{background-position:-64px -128px}.ui-icon-close{background-position:-80px -128px}.ui-icon-closethick{background-position:-96px -128px}.ui-icon-key{background-position:-112px -128px}.ui-icon-lightbulb{background-position:-128px -128px}.ui-icon-scissors{background-position:-144px -128px}.ui-icon-clipboard{background-position:-160px -128px}.ui-icon-copy{background-position:-176px -128px}.ui-icon-contact{background-position:-192px -128px}.ui-icon-image{background-position:-208px -128px}.ui-icon-video{background-position:-224px -128px}.ui-icon-script{background-position:-240px -128px}.ui-icon-alert{background-position:0 -144px}.ui-icon-info{background-position:-16px -144px}.ui-icon-notice{background-position:-32px -144px}.ui-icon-help{background-position:-48px -144px}.ui-icon-check{background-position:-64px -144px}.ui-icon-bullet{background-position:-80px -144px}.ui-icon-radio-on{background-position:-96px -144px}.ui-icon-radio-off{background-position:-112px -144px}.ui-icon-pin-w{background-position:-128px -144px}.ui-icon-pin-s{background-position:-144px -144px}.ui-icon-play{background-position:0 -160px}.ui-icon-pause{background-position:-16px -160px}.ui-icon-seek-next{background-position:-32px -160px}.ui-icon-seek-prev{background-position:-48px -160px}.ui-icon-seek-end{background-position:-64px -160px}.ui-icon-seek-start{background-position:-80px -160px}.ui-icon-seek-first{background-position:-80px -160px}.ui-icon-stop{background-position:-96px -160px}.ui-icon-eject{background-position:-112px -160px}.ui-icon-volume-off{background-position:-128px -160px}.ui-icon-volume-on{background-position:-144px -160px}.ui-icon-power{background-position:0 -176px}.ui-icon-signal-diag{background-position:-16px -176px}.ui-icon-signal{background-position:-32px -176px}.ui-icon-battery-0{background-position:-48px -176px}.ui-icon-battery-1{background-position:-64px -176px}.ui-icon-battery-2{background-position:-80px -176px}.ui-icon-battery-3{background-position:-96px -176px}.ui-icon-circle-plus{background-position:0 -192px}.ui-icon-circle-minus{background-position:-16px -192px}.ui-icon-circle-close{background-position:-32px -192px}.ui-icon-circle-triangle-e{background-position:-48px -192px}.ui-icon-circle-triangle-s{background-position:-64px -192px}.ui-icon-circle-triangle-w{background-position:-80px -192px}.ui-icon-circle-triangle-n{background-position:-96px -192px}.ui-icon-circle-arrow-e{background-position:-112px -192px}.ui-icon-circle-arrow-s{background-position:-128px -192px}.ui-icon-circle-arrow-w{background-position:-144px -192px}.ui-icon-circle-arrow-n{background-position:-160px -192px}.ui-icon-circle-zoomin{background-position:-176px -192px}.ui-icon-circle-zoomout{background-position:-192px -192px}.ui-icon-circle-check{background-position:-208px -192px}.ui-icon-circlesmall-plus{background-position:0 -208px}.ui-icon-circlesmall-minus{background-position:-16px -208px}.ui-icon-circlesmall-close{background-position:-32px -208px}.ui-icon-squaresmall-plus{background-position:-48px -208px}.ui-icon-squaresmall-minus{background-position:-64px -208px}.ui-icon-squaresmall-close{background-position:-80px -208px}.ui-icon-grip-dotted-vertical{background-position:0 -224px}.ui-icon-grip-dotted-horizontal{background-position:-16px -224px}.ui-icon-grip-solid-vertical{background-position:-32px -224px}.ui-icon-grip-solid-horizontal{background-position:-48px -224px}.ui-icon-gripsmall-diagonal-se{background-position:-64px -224px}.ui-icon-grip-diagonal-se{background-position:-80px -224px}.ui-corner-all,.ui-corner-top,.ui-corner-left,.ui-corner-tl{border-top-left-radius:3px}.ui-corner-all,.ui-corner-top,.ui-corner-right,.ui-corner-tr{border-top-right-radius:3px}.ui-corner-all,.ui-corner-bottom,.ui-corner-left,.ui-corner-bl{border-bottom-left-radius:3px}.ui-corner-all,.ui-corner-bottom,.ui-corner-right,.ui-corner-br{border-bottom-right-radius:3px}.ui-widget-overlay{background:#aaa;opacity:.3;filter:Alpha(Opacity=30)}.ui-widget-shadow{-webkit-box-shadow:0 0 5px #666;box-shadow:0 0 5px #666} -------------------------------------------------------------------------------- /www/jquery-ui.min.js: -------------------------------------------------------------------------------- 1 | /*! jQuery UI - v1.12.1 - 2017-07-01 2 | * http://jqueryui.com 3 | * Includes: widget.js, keycode.js, widgets/mouse.js, widgets/slider.js 4 | * Copyright jQuery Foundation and other contributors; Licensed MIT */ 5 | 6 | (function(t){"function"==typeof define&&define.amd?define(["jquery"],t):t(jQuery)})(function(t){t.ui=t.ui||{},t.ui.version="1.12.1";var e=0,i=Array.prototype.slice;t.cleanData=function(e){return function(i){var s,n,o;for(o=0;null!=(n=i[o]);o++)try{s=t._data(n,"events"),s&&s.remove&&t(n).triggerHandler("remove")}catch(a){}e(i)}}(t.cleanData),t.widget=function(e,i,s){var n,o,a,r={},l=e.split(".")[0];e=e.split(".")[1];var h=l+"-"+e;return s||(s=i,i=t.Widget),t.isArray(s)&&(s=t.extend.apply(null,[{}].concat(s))),t.expr[":"][h.toLowerCase()]=function(e){return!!t.data(e,h)},t[l]=t[l]||{},n=t[l][e],o=t[l][e]=function(t,e){return this._createWidget?(arguments.length&&this._createWidget(t,e),void 0):new o(t,e)},t.extend(o,n,{version:s.version,_proto:t.extend({},s),_childConstructors:[]}),a=new i,a.options=t.widget.extend({},a.options),t.each(s,function(e,s){return t.isFunction(s)?(r[e]=function(){function t(){return i.prototype[e].apply(this,arguments)}function n(t){return i.prototype[e].apply(this,t)}return function(){var e,i=this._super,o=this._superApply;return this._super=t,this._superApply=n,e=s.apply(this,arguments),this._super=i,this._superApply=o,e}}(),void 0):(r[e]=s,void 0)}),o.prototype=t.widget.extend(a,{widgetEventPrefix:n?a.widgetEventPrefix||e:e},r,{constructor:o,namespace:l,widgetName:e,widgetFullName:h}),n?(t.each(n._childConstructors,function(e,i){var s=i.prototype;t.widget(s.namespace+"."+s.widgetName,o,i._proto)}),delete n._childConstructors):i._childConstructors.push(o),t.widget.bridge(e,o),o},t.widget.extend=function(e){for(var s,n,o=i.call(arguments,1),a=0,r=o.length;r>a;a++)for(s in o[a])n=o[a][s],o[a].hasOwnProperty(s)&&void 0!==n&&(e[s]=t.isPlainObject(n)?t.isPlainObject(e[s])?t.widget.extend({},e[s],n):t.widget.extend({},n):n);return e},t.widget.bridge=function(e,s){var n=s.prototype.widgetFullName||e;t.fn[e]=function(o){var a="string"==typeof o,r=i.call(arguments,1),l=this;return a?this.length||"instance"!==o?this.each(function(){var i,s=t.data(this,n);return"instance"===o?(l=s,!1):s?t.isFunction(s[o])&&"_"!==o.charAt(0)?(i=s[o].apply(s,r),i!==s&&void 0!==i?(l=i&&i.jquery?l.pushStack(i.get()):i,!1):void 0):t.error("no such method '"+o+"' for "+e+" widget instance"):t.error("cannot call methods on "+e+" prior to initialization; "+"attempted to call method '"+o+"'")}):l=void 0:(r.length&&(o=t.widget.extend.apply(null,[o].concat(r))),this.each(function(){var e=t.data(this,n);e?(e.option(o||{}),e._init&&e._init()):t.data(this,n,new s(o,this))})),l}},t.Widget=function(){},t.Widget._childConstructors=[],t.Widget.prototype={widgetName:"widget",widgetEventPrefix:"",defaultElement:"
",options:{classes:{},disabled:!1,create:null},_createWidget:function(i,s){s=t(s||this.defaultElement||this)[0],this.element=t(s),this.uuid=e++,this.eventNamespace="."+this.widgetName+this.uuid,this.bindings=t(),this.hoverable=t(),this.focusable=t(),this.classesElementLookup={},s!==this&&(t.data(s,this.widgetFullName,this),this._on(!0,this.element,{remove:function(t){t.target===s&&this.destroy()}}),this.document=t(s.style?s.ownerDocument:s.document||s),this.window=t(this.document[0].defaultView||this.document[0].parentWindow)),this.options=t.widget.extend({},this.options,this._getCreateOptions(),i),this._create(),this.options.disabled&&this._setOptionDisabled(this.options.disabled),this._trigger("create",null,this._getCreateEventData()),this._init()},_getCreateOptions:function(){return{}},_getCreateEventData:t.noop,_create:t.noop,_init:t.noop,destroy:function(){var e=this;this._destroy(),t.each(this.classesElementLookup,function(t,i){e._removeClass(i,t)}),this.element.off(this.eventNamespace).removeData(this.widgetFullName),this.widget().off(this.eventNamespace).removeAttr("aria-disabled"),this.bindings.off(this.eventNamespace)},_destroy:t.noop,widget:function(){return this.element},option:function(e,i){var s,n,o,a=e;if(0===arguments.length)return t.widget.extend({},this.options);if("string"==typeof e)if(a={},s=e.split("."),e=s.shift(),s.length){for(n=a[e]=t.widget.extend({},this.options[e]),o=0;s.length-1>o;o++)n[s[o]]=n[s[o]]||{},n=n[s[o]];if(e=s.pop(),1===arguments.length)return void 0===n[e]?null:n[e];n[e]=i}else{if(1===arguments.length)return void 0===this.options[e]?null:this.options[e];a[e]=i}return this._setOptions(a),this},_setOptions:function(t){var e;for(e in t)this._setOption(e,t[e]);return this},_setOption:function(t,e){return"classes"===t&&this._setOptionClasses(e),this.options[t]=e,"disabled"===t&&this._setOptionDisabled(e),this},_setOptionClasses:function(e){var i,s,n;for(i in e)n=this.classesElementLookup[i],e[i]!==this.options.classes[i]&&n&&n.length&&(s=t(n.get()),this._removeClass(n,i),s.addClass(this._classes({element:s,keys:i,classes:e,add:!0})))},_setOptionDisabled:function(t){this._toggleClass(this.widget(),this.widgetFullName+"-disabled",null,!!t),t&&(this._removeClass(this.hoverable,null,"ui-state-hover"),this._removeClass(this.focusable,null,"ui-state-focus"))},enable:function(){return this._setOptions({disabled:!1})},disable:function(){return this._setOptions({disabled:!0})},_classes:function(e){function i(i,o){var a,r;for(r=0;i.length>r;r++)a=n.classesElementLookup[i[r]]||t(),a=e.add?t(t.unique(a.get().concat(e.element.get()))):t(a.not(e.element).get()),n.classesElementLookup[i[r]]=a,s.push(i[r]),o&&e.classes[i[r]]&&s.push(e.classes[i[r]])}var s=[],n=this;return e=t.extend({element:this.element,classes:this.options.classes||{}},e),this._on(e.element,{remove:"_untrackClassesElement"}),e.keys&&i(e.keys.match(/\S+/g)||[],!0),e.extra&&i(e.extra.match(/\S+/g)||[]),s.join(" ")},_untrackClassesElement:function(e){var i=this;t.each(i.classesElementLookup,function(s,n){-1!==t.inArray(e.target,n)&&(i.classesElementLookup[s]=t(n.not(e.target).get()))})},_removeClass:function(t,e,i){return this._toggleClass(t,e,i,!1)},_addClass:function(t,e,i){return this._toggleClass(t,e,i,!0)},_toggleClass:function(t,e,i,s){s="boolean"==typeof s?s:i;var n="string"==typeof t||null===t,o={extra:n?e:i,keys:n?t:e,element:n?this.element:t,add:s};return o.element.toggleClass(this._classes(o),s),this},_on:function(e,i,s){var n,o=this;"boolean"!=typeof e&&(s=i,i=e,e=!1),s?(i=n=t(i),this.bindings=this.bindings.add(i)):(s=i,i=this.element,n=this.widget()),t.each(s,function(s,a){function r(){return e||o.options.disabled!==!0&&!t(this).hasClass("ui-state-disabled")?("string"==typeof a?o[a]:a).apply(o,arguments):void 0}"string"!=typeof a&&(r.guid=a.guid=a.guid||r.guid||t.guid++);var l=s.match(/^([\w:-]*)\s*(.*)$/),h=l[1]+o.eventNamespace,c=l[2];c?n.on(h,c,r):i.on(h,r)})},_off:function(e,i){i=(i||"").split(" ").join(this.eventNamespace+" ")+this.eventNamespace,e.off(i).off(i),this.bindings=t(this.bindings.not(e).get()),this.focusable=t(this.focusable.not(e).get()),this.hoverable=t(this.hoverable.not(e).get())},_delay:function(t,e){function i(){return("string"==typeof t?s[t]:t).apply(s,arguments)}var s=this;return setTimeout(i,e||0)},_hoverable:function(e){this.hoverable=this.hoverable.add(e),this._on(e,{mouseenter:function(e){this._addClass(t(e.currentTarget),null,"ui-state-hover")},mouseleave:function(e){this._removeClass(t(e.currentTarget),null,"ui-state-hover")}})},_focusable:function(e){this.focusable=this.focusable.add(e),this._on(e,{focusin:function(e){this._addClass(t(e.currentTarget),null,"ui-state-focus")},focusout:function(e){this._removeClass(t(e.currentTarget),null,"ui-state-focus")}})},_trigger:function(e,i,s){var n,o,a=this.options[e];if(s=s||{},i=t.Event(i),i.type=(e===this.widgetEventPrefix?e:this.widgetEventPrefix+e).toLowerCase(),i.target=this.element[0],o=i.originalEvent)for(n in o)n in i||(i[n]=o[n]);return this.element.trigger(i,s),!(t.isFunction(a)&&a.apply(this.element[0],[i].concat(s))===!1||i.isDefaultPrevented())}},t.each({show:"fadeIn",hide:"fadeOut"},function(e,i){t.Widget.prototype["_"+e]=function(s,n,o){"string"==typeof n&&(n={effect:n});var a,r=n?n===!0||"number"==typeof n?i:n.effect||i:e;n=n||{},"number"==typeof n&&(n={duration:n}),a=!t.isEmptyObject(n),n.complete=o,n.delay&&s.delay(n.delay),a&&t.effects&&t.effects.effect[r]?s[e](n):r!==e&&s[r]?s[r](n.duration,n.easing,o):s.queue(function(i){t(this)[e](),o&&o.call(s[0]),i()})}}),t.widget,t.ui.keyCode={BACKSPACE:8,COMMA:188,DELETE:46,DOWN:40,END:35,ENTER:13,ESCAPE:27,HOME:36,LEFT:37,PAGE_DOWN:34,PAGE_UP:33,PERIOD:190,RIGHT:39,SPACE:32,TAB:9,UP:38},t.ui.ie=!!/msie [\w.]+/.exec(navigator.userAgent.toLowerCase());var s=!1;t(document).on("mouseup",function(){s=!1}),t.widget("ui.mouse",{version:"1.12.1",options:{cancel:"input, textarea, button, select, option",distance:1,delay:0},_mouseInit:function(){var e=this;this.element.on("mousedown."+this.widgetName,function(t){return e._mouseDown(t)}).on("click."+this.widgetName,function(i){return!0===t.data(i.target,e.widgetName+".preventClickEvent")?(t.removeData(i.target,e.widgetName+".preventClickEvent"),i.stopImmediatePropagation(),!1):void 0}),this.started=!1},_mouseDestroy:function(){this.element.off("."+this.widgetName),this._mouseMoveDelegate&&this.document.off("mousemove."+this.widgetName,this._mouseMoveDelegate).off("mouseup."+this.widgetName,this._mouseUpDelegate)},_mouseDown:function(e){if(!s){this._mouseMoved=!1,this._mouseStarted&&this._mouseUp(e),this._mouseDownEvent=e;var i=this,n=1===e.which,o="string"==typeof this.options.cancel&&e.target.nodeName?t(e.target).closest(this.options.cancel).length:!1;return n&&!o&&this._mouseCapture(e)?(this.mouseDelayMet=!this.options.delay,this.mouseDelayMet||(this._mouseDelayTimer=setTimeout(function(){i.mouseDelayMet=!0},this.options.delay)),this._mouseDistanceMet(e)&&this._mouseDelayMet(e)&&(this._mouseStarted=this._mouseStart(e)!==!1,!this._mouseStarted)?(e.preventDefault(),!0):(!0===t.data(e.target,this.widgetName+".preventClickEvent")&&t.removeData(e.target,this.widgetName+".preventClickEvent"),this._mouseMoveDelegate=function(t){return i._mouseMove(t)},this._mouseUpDelegate=function(t){return i._mouseUp(t)},this.document.on("mousemove."+this.widgetName,this._mouseMoveDelegate).on("mouseup."+this.widgetName,this._mouseUpDelegate),e.preventDefault(),s=!0,!0)):!0}},_mouseMove:function(e){if(this._mouseMoved){if(t.ui.ie&&(!document.documentMode||9>document.documentMode)&&!e.button)return this._mouseUp(e);if(!e.which)if(e.originalEvent.altKey||e.originalEvent.ctrlKey||e.originalEvent.metaKey||e.originalEvent.shiftKey)this.ignoreMissingWhich=!0;else if(!this.ignoreMissingWhich)return this._mouseUp(e)}return(e.which||e.button)&&(this._mouseMoved=!0),this._mouseStarted?(this._mouseDrag(e),e.preventDefault()):(this._mouseDistanceMet(e)&&this._mouseDelayMet(e)&&(this._mouseStarted=this._mouseStart(this._mouseDownEvent,e)!==!1,this._mouseStarted?this._mouseDrag(e):this._mouseUp(e)),!this._mouseStarted)},_mouseUp:function(e){this.document.off("mousemove."+this.widgetName,this._mouseMoveDelegate).off("mouseup."+this.widgetName,this._mouseUpDelegate),this._mouseStarted&&(this._mouseStarted=!1,e.target===this._mouseDownEvent.target&&t.data(e.target,this.widgetName+".preventClickEvent",!0),this._mouseStop(e)),this._mouseDelayTimer&&(clearTimeout(this._mouseDelayTimer),delete this._mouseDelayTimer),this.ignoreMissingWhich=!1,s=!1,e.preventDefault()},_mouseDistanceMet:function(t){return Math.max(Math.abs(this._mouseDownEvent.pageX-t.pageX),Math.abs(this._mouseDownEvent.pageY-t.pageY))>=this.options.distance},_mouseDelayMet:function(){return this.mouseDelayMet},_mouseStart:function(){},_mouseDrag:function(){},_mouseStop:function(){},_mouseCapture:function(){return!0}}),t.widget("ui.slider",t.ui.mouse,{version:"1.12.1",widgetEventPrefix:"slide",options:{animate:!1,classes:{"ui-slider":"ui-corner-all","ui-slider-handle":"ui-corner-all","ui-slider-range":"ui-corner-all ui-widget-header"},distance:0,max:100,min:0,orientation:"horizontal",range:!1,step:1,value:0,values:null,change:null,slide:null,start:null,stop:null},numPages:5,_create:function(){this._keySliding=!1,this._mouseSliding=!1,this._animateOff=!0,this._handleIndex=null,this._detectOrientation(),this._mouseInit(),this._calculateNewMax(),this._addClass("ui-slider ui-slider-"+this.orientation,"ui-widget ui-widget-content"),this._refresh(),this._animateOff=!1},_refresh:function(){this._createRange(),this._createHandles(),this._setupEvents(),this._refreshValue()},_createHandles:function(){var e,i,s=this.options,n=this.element.find(".ui-slider-handle"),o="",a=[];for(i=s.values&&s.values.length||1,n.length>i&&(n.slice(i).remove(),n=n.slice(0,i)),e=n.length;i>e;e++)a.push(o);this.handles=n.add(t(a.join("")).appendTo(this.element)),this._addClass(this.handles,"ui-slider-handle","ui-state-default"),this.handle=this.handles.eq(0),this.handles.each(function(e){t(this).data("ui-slider-handle-index",e).attr("tabIndex",0)})},_createRange:function(){var e=this.options;e.range?(e.range===!0&&(e.values?e.values.length&&2!==e.values.length?e.values=[e.values[0],e.values[0]]:t.isArray(e.values)&&(e.values=e.values.slice(0)):e.values=[this._valueMin(),this._valueMin()]),this.range&&this.range.length?(this._removeClass(this.range,"ui-slider-range-min ui-slider-range-max"),this.range.css({left:"",bottom:""})):(this.range=t("
").appendTo(this.element),this._addClass(this.range,"ui-slider-range")),("min"===e.range||"max"===e.range)&&this._addClass(this.range,"ui-slider-range-"+e.range)):(this.range&&this.range.remove(),this.range=null)},_setupEvents:function(){this._off(this.handles),this._on(this.handles,this._handleEvents),this._hoverable(this.handles),this._focusable(this.handles)},_destroy:function(){this.handles.remove(),this.range&&this.range.remove(),this._mouseDestroy()},_mouseCapture:function(e){var i,s,n,o,a,r,l,h,c=this,u=this.options;return u.disabled?!1:(this.elementSize={width:this.element.outerWidth(),height:this.element.outerHeight()},this.elementOffset=this.element.offset(),i={x:e.pageX,y:e.pageY},s=this._normValueFromMouse(i),n=this._valueMax()-this._valueMin()+1,this.handles.each(function(e){var i=Math.abs(s-c.values(e));(n>i||n===i&&(e===c._lastChangedValue||c.values(e)===u.min))&&(n=i,o=t(this),a=e)}),r=this._start(e,a),r===!1?!1:(this._mouseSliding=!0,this._handleIndex=a,this._addClass(o,null,"ui-state-active"),o.trigger("focus"),l=o.offset(),h=!t(e.target).parents().addBack().is(".ui-slider-handle"),this._clickOffset=h?{left:0,top:0}:{left:e.pageX-l.left-o.width()/2,top:e.pageY-l.top-o.height()/2-(parseInt(o.css("borderTopWidth"),10)||0)-(parseInt(o.css("borderBottomWidth"),10)||0)+(parseInt(o.css("marginTop"),10)||0)},this.handles.hasClass("ui-state-hover")||this._slide(e,a,s),this._animateOff=!0,!0))},_mouseStart:function(){return!0},_mouseDrag:function(t){var e={x:t.pageX,y:t.pageY},i=this._normValueFromMouse(e);return this._slide(t,this._handleIndex,i),!1},_mouseStop:function(t){return this._removeClass(this.handles,null,"ui-state-active"),this._mouseSliding=!1,this._stop(t,this._handleIndex),this._change(t,this._handleIndex),this._handleIndex=null,this._clickOffset=null,this._animateOff=!1,!1},_detectOrientation:function(){this.orientation="vertical"===this.options.orientation?"vertical":"horizontal"},_normValueFromMouse:function(t){var e,i,s,n,o;return"horizontal"===this.orientation?(e=this.elementSize.width,i=t.x-this.elementOffset.left-(this._clickOffset?this._clickOffset.left:0)):(e=this.elementSize.height,i=t.y-this.elementOffset.top-(this._clickOffset?this._clickOffset.top:0)),s=i/e,s>1&&(s=1),0>s&&(s=0),"vertical"===this.orientation&&(s=1-s),n=this._valueMax()-this._valueMin(),o=this._valueMin()+s*n,this._trimAlignValue(o)},_uiHash:function(t,e,i){var s={handle:this.handles[t],handleIndex:t,value:void 0!==e?e:this.value()};return this._hasMultipleValues()&&(s.value=void 0!==e?e:this.values(t),s.values=i||this.values()),s},_hasMultipleValues:function(){return this.options.values&&this.options.values.length},_start:function(t,e){return this._trigger("start",t,this._uiHash(e))},_slide:function(t,e,i){var s,n,o=this.value(),a=this.values();this._hasMultipleValues()&&(n=this.values(e?0:1),o=this.values(e),2===this.options.values.length&&this.options.range===!0&&(i=0===e?Math.min(n,i):Math.max(n,i)),a[e]=i),i!==o&&(s=this._trigger("slide",t,this._uiHash(e,i,a)),s!==!1&&(this._hasMultipleValues()?this.values(e,i):this.value(i)))},_stop:function(t,e){this._trigger("stop",t,this._uiHash(e))},_change:function(t,e){this._keySliding||this._mouseSliding||(this._lastChangedValue=e,this._trigger("change",t,this._uiHash(e)))},value:function(t){return arguments.length?(this.options.value=this._trimAlignValue(t),this._refreshValue(),this._change(null,0),void 0):this._value()},values:function(e,i){var s,n,o;if(arguments.length>1)return this.options.values[e]=this._trimAlignValue(i),this._refreshValue(),this._change(null,e),void 0;if(!arguments.length)return this._values();if(!t.isArray(arguments[0]))return this._hasMultipleValues()?this._values(e):this.value();for(s=this.options.values,n=arguments[0],o=0;s.length>o;o+=1)s[o]=this._trimAlignValue(n[o]),this._change(null,o);this._refreshValue()},_setOption:function(e,i){var s,n=0;switch("range"===e&&this.options.range===!0&&("min"===i?(this.options.value=this._values(0),this.options.values=null):"max"===i&&(this.options.value=this._values(this.options.values.length-1),this.options.values=null)),t.isArray(this.options.values)&&(n=this.options.values.length),this._super(e,i),e){case"orientation":this._detectOrientation(),this._removeClass("ui-slider-horizontal ui-slider-vertical")._addClass("ui-slider-"+this.orientation),this._refreshValue(),this.options.range&&this._refreshRange(i),this.handles.css("horizontal"===i?"bottom":"left","");break;case"value":this._animateOff=!0,this._refreshValue(),this._change(null,0),this._animateOff=!1;break;case"values":for(this._animateOff=!0,this._refreshValue(),s=n-1;s>=0;s--)this._change(null,s);this._animateOff=!1;break;case"step":case"min":case"max":this._animateOff=!0,this._calculateNewMax(),this._refreshValue(),this._animateOff=!1;break;case"range":this._animateOff=!0,this._refresh(),this._animateOff=!1}},_setOptionDisabled:function(t){this._super(t),this._toggleClass(null,"ui-state-disabled",!!t)},_value:function(){var t=this.options.value;return t=this._trimAlignValue(t)},_values:function(t){var e,i,s;if(arguments.length)return e=this.options.values[t],e=this._trimAlignValue(e);if(this._hasMultipleValues()){for(i=this.options.values.slice(),s=0;i.length>s;s+=1)i[s]=this._trimAlignValue(i[s]);return i}return[]},_trimAlignValue:function(t){if(this._valueMin()>=t)return this._valueMin();if(t>=this._valueMax())return this._valueMax();var e=this.options.step>0?this.options.step:1,i=(t-this._valueMin())%e,s=t-i;return 2*Math.abs(i)>=e&&(s+=i>0?e:-e),parseFloat(s.toFixed(5))},_calculateNewMax:function(){var t=this.options.max,e=this._valueMin(),i=this.options.step,s=Math.round((t-e)/i)*i;t=s+e,t>this.options.max&&(t-=i),this.max=parseFloat(t.toFixed(this._precision()))},_precision:function(){var t=this._precisionOf(this.options.step);return null!==this.options.min&&(t=Math.max(t,this._precisionOf(this.options.min))),t},_precisionOf:function(t){var e=""+t,i=e.indexOf(".");return-1===i?0:e.length-i-1},_valueMin:function(){return this.options.min},_valueMax:function(){return this.max},_refreshRange:function(t){"vertical"===t&&this.range.css({width:"",left:""}),"horizontal"===t&&this.range.css({height:"",bottom:""})},_refreshValue:function(){var e,i,s,n,o,a=this.options.range,r=this.options,l=this,h=this._animateOff?!1:r.animate,c={};this._hasMultipleValues()?this.handles.each(function(s){i=100*((l.values(s)-l._valueMin())/(l._valueMax()-l._valueMin())),c["horizontal"===l.orientation?"left":"bottom"]=i+"%",t(this).stop(1,1)[h?"animate":"css"](c,r.animate),l.options.range===!0&&("horizontal"===l.orientation?(0===s&&l.range.stop(1,1)[h?"animate":"css"]({left:i+"%"},r.animate),1===s&&l.range[h?"animate":"css"]({width:i-e+"%"},{queue:!1,duration:r.animate})):(0===s&&l.range.stop(1,1)[h?"animate":"css"]({bottom:i+"%"},r.animate),1===s&&l.range[h?"animate":"css"]({height:i-e+"%"},{queue:!1,duration:r.animate}))),e=i}):(s=this.value(),n=this._valueMin(),o=this._valueMax(),i=o!==n?100*((s-n)/(o-n)):0,c["horizontal"===this.orientation?"left":"bottom"]=i+"%",this.handle.stop(1,1)[h?"animate":"css"](c,r.animate),"min"===a&&"horizontal"===this.orientation&&this.range.stop(1,1)[h?"animate":"css"]({width:i+"%"},r.animate),"max"===a&&"horizontal"===this.orientation&&this.range.stop(1,1)[h?"animate":"css"]({width:100-i+"%"},r.animate),"min"===a&&"vertical"===this.orientation&&this.range.stop(1,1)[h?"animate":"css"]({height:i+"%"},r.animate),"max"===a&&"vertical"===this.orientation&&this.range.stop(1,1)[h?"animate":"css"]({height:100-i+"%"},r.animate))},_handleEvents:{keydown:function(e){var i,s,n,o,a=t(e.target).data("ui-slider-handle-index");switch(e.keyCode){case t.ui.keyCode.HOME:case t.ui.keyCode.END:case t.ui.keyCode.PAGE_UP:case t.ui.keyCode.PAGE_DOWN:case t.ui.keyCode.UP:case t.ui.keyCode.RIGHT:case t.ui.keyCode.DOWN:case t.ui.keyCode.LEFT:if(e.preventDefault(),!this._keySliding&&(this._keySliding=!0,this._addClass(t(e.target),null,"ui-state-active"),i=this._start(e,a),i===!1))return}switch(o=this.options.step,s=n=this._hasMultipleValues()?this.values(a):this.value(),e.keyCode){case t.ui.keyCode.HOME:n=this._valueMin();break;case t.ui.keyCode.END:n=this._valueMax();break;case t.ui.keyCode.PAGE_UP:n=this._trimAlignValue(s+(this._valueMax()-this._valueMin())/this.numPages);break;case t.ui.keyCode.PAGE_DOWN:n=this._trimAlignValue(s-(this._valueMax()-this._valueMin())/this.numPages);break;case t.ui.keyCode.UP:case t.ui.keyCode.RIGHT:if(s===this._valueMax())return;n=this._trimAlignValue(s+o);break;case t.ui.keyCode.DOWN:case t.ui.keyCode.LEFT:if(s===this._valueMin())return;n=this._trimAlignValue(s-o)}this._slide(e,a,n)},keyup:function(e){var i=t(e.target).data("ui-slider-handle-index");this._keySliding&&(this._keySliding=!1,this._stop(e,i),this._change(e,i),this._removeClass(t(e.target),null,"ui-state-active"))}}})}); -------------------------------------------------------------------------------- /www/jquery.timepicker.css: -------------------------------------------------------------------------------- 1 | .ui-timepicker-wrapper { 2 | overflow-y: auto; 3 | max-height: 150px; 4 | width: 6.5em; 5 | background: #fff; 6 | border: 1px solid #ddd; 7 | -webkit-box-shadow:0 5px 10px rgba(0,0,0,0.2); 8 | -moz-box-shadow:0 5px 10px rgba(0,0,0,0.2); 9 | box-shadow:0 5px 10px rgba(0,0,0,0.2); 10 | outline: none; 11 | z-index: 10001; 12 | margin: 0; 13 | } 14 | 15 | .ui-timepicker-wrapper.ui-timepicker-with-duration { 16 | width: 13em; 17 | } 18 | 19 | .ui-timepicker-wrapper.ui-timepicker-with-duration.ui-timepicker-step-30, 20 | .ui-timepicker-wrapper.ui-timepicker-with-duration.ui-timepicker-step-60 { 21 | width: 11em; 22 | } 23 | 24 | .ui-timepicker-list { 25 | margin: 0; 26 | padding: 0; 27 | list-style: none; 28 | } 29 | 30 | .ui-timepicker-duration { 31 | margin-left: 5px; color: #888; 32 | } 33 | 34 | .ui-timepicker-list:hover .ui-timepicker-duration { 35 | color: #888; 36 | } 37 | 38 | .ui-timepicker-list li { 39 | padding: 3px 0 3px 5px; 40 | cursor: pointer; 41 | white-space: nowrap; 42 | color: #000; 43 | list-style: none; 44 | margin: 0; 45 | } 46 | 47 | .ui-timepicker-list:hover .ui-timepicker-selected { 48 | background: #fff; color: #000; 49 | } 50 | 51 | li.ui-timepicker-selected, 52 | .ui-timepicker-list li:hover, 53 | .ui-timepicker-list .ui-timepicker-selected:hover { 54 | background: #1980EC; color: #fff; 55 | } 56 | 57 | li.ui-timepicker-selected .ui-timepicker-duration, 58 | .ui-timepicker-list li:hover .ui-timepicker-duration { 59 | color: #ccc; 60 | } 61 | 62 | .ui-timepicker-list li.ui-timepicker-disabled, 63 | .ui-timepicker-list li.ui-timepicker-disabled:hover, 64 | .ui-timepicker-list li.ui-timepicker-selected.ui-timepicker-disabled { 65 | color: #888; 66 | cursor: default; 67 | } 68 | 69 | .ui-timepicker-list li.ui-timepicker-disabled:hover, 70 | .ui-timepicker-list li.ui-timepicker-selected.ui-timepicker-disabled { 71 | background: #f2f2f2; 72 | } 73 | -------------------------------------------------------------------------------- /www/jquery.timepicker.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * jquery-timepicker v1.11.11 - A jQuery timepicker plugin inspired by Google Calendar. It supports both mouse and keyboard navigation. 3 | * Copyright (c) 2017 Jon Thornton - http://jonthornton.github.com/jquery-timepicker/ 4 | * License: MIT 5 | */ 6 | 7 | !function(a){"object"==typeof exports&&exports&&"object"==typeof module&&module&&module.exports===exports?a(require("jquery")):"function"==typeof define&&define.amd?define(["jquery"],a):a(jQuery)}(function(a){function b(a){var b=a[0];return b.offsetWidth>0&&b.offsetHeight>0}function c(b){if(b.minTime&&(b.minTime=t(b.minTime)),b.maxTime&&(b.maxTime=t(b.maxTime)),b.durationTime&&"function"!=typeof b.durationTime&&(b.durationTime=t(b.durationTime)),"now"==b.scrollDefault)b.scrollDefault=function(){return b.roundingFunction(t(new Date),b)};else if(b.scrollDefault&&"function"!=typeof b.scrollDefault){var c=b.scrollDefault;b.scrollDefault=function(){return b.roundingFunction(t(c),b)}}else b.minTime&&(b.scrollDefault=function(){return b.roundingFunction(b.minTime,b)});if("string"===a.type(b.timeFormat)&&b.timeFormat.match(/[gh]/)&&(b._twelveHourTime=!0),b.showOnFocus===!1&&-1!=b.showOn.indexOf("focus")&&b.showOn.splice(b.showOn.indexOf("focus"),1),b.disableTimeRanges.length>0){for(var d in b.disableTimeRanges)b.disableTimeRanges[d]=[t(b.disableTimeRanges[d][0]),t(b.disableTimeRanges[d][1])];b.disableTimeRanges=b.disableTimeRanges.sort(function(a,b){return a[0]-b[0]});for(var d=b.disableTimeRanges.length-1;d>0;d--)b.disableTimeRanges[d][0]<=b.disableTimeRanges[d-1][1]&&(b.disableTimeRanges[d-1]=[Math.min(b.disableTimeRanges[d][0],b.disableTimeRanges[d-1][0]),Math.max(b.disableTimeRanges[d][1],b.disableTimeRanges[d-1][1])],b.disableTimeRanges.splice(d,1))}return b}function d(b){var c=b.data("timepicker-settings"),d=b.data("timepicker-list");if(d&&d.length&&(d.remove(),b.data("timepicker-list",!1)),c.useSelect){d=a("

67 |

Note: these buttons are no longer up-to-date; they might not include new classes.

68 | 69 | 70 |
71 | 72 | 74 |
75 | 76 |
77 | 78 |
79 | 80 |
81 | 82 |

Loading...

83 | 84 | 111 | 112 | 113 | -------------------------------------------------------------------------------- /www/old_www/hass-a.js: -------------------------------------------------------------------------------- 1 | var hass_a = {"4.021": null, "4.02A": null, "4.110": null, "4.301": null, "4.312": null, "4.314": null, "4.320": null, "4.322": null, "4.330": null, "4.332": null, "4.341": null, "4.344": null, "4.352": null, "4.354": null, "4.356": null, "4.361": null, "4.368": null, "4.373": null, "4.601": null, "4.602": null, "4.603": null, "4.605": null, "4.606": null, "4.609": null, "4.610": null, "4.614": null, "4.635": null, "4.641": null, "4.648": null, "4.651": null, "6.073": null, "6.809": null, "24.912": null, "21A.507": null, "21A.550": null, "21A.551": null, "21G.049": null, "21G.409": null, "21H.106": null, "21L.005": null, "21L.008": null, "21L.011": null, "21L.013": null, "21L.023": null, "21L.486": null, "21L.489": null, "21M.011": null, "21M.013": null, "21M.030": null, "21M.051": null, "21M.065": null, "21M.215": null, "21M.220": null, "21M.223": null, "21M.226": null, "21M.235": null, "21M.250": null, "21M.260": null, "21M.269": null, "21M.271": null, "21M.273": null, "21M.283": null, "21M.284": null, "21M.289": null, "21M.291": null, "21M.292": null, "21M.293": null, "21M.294": null, "21M.295": null, "21M.299": null, "21M.301": null, "21M.302": null, "21M.303": null, "21M.304": null, "21M.310": null, "21M.340": null, "21M.341": null, "21M.342": null, "21M.351": null, "21M.355": null, "21M.359": null, "21M.361": null, "21M.362": null, "21M.380": null, "21M.385": null, "21M.500": null, "21M.600": null, "21M.601": null, "21M.603": null, "21M.604": null, "21M.605": null, "21M.606": null, "21M.611": null, "21M.624": null, "21M.645": null, "21M.690": null, "21M.700": null, "21M.704": null, "21M.705": null, "21M.710": null, "21M.711": null, "21M.732": null, "21M.733": null, "21M.734": null, "21M.735": null, "21M.785": null, "21M.790": null, "21M.800": null, "21M.830": null, "21M.840": null, "21M.846": null, "21W.740": null, "21W.741": null, "21W.744": null, "21W.748": null, "21W.749": null, "21W.750": null, "21W.751": null, "21W.752": null, "21W.753": null, "21W.754": null, "21W.755": null, "21W.756": null, "21W.757": null, "21W.758": null, "21W.759": null, "21W.762": null, "21W.763": null, "21W.764": null, "21W.765": null, "21W.769": null, "21W.770": null, "21W.771": null, "21W.773": null, "21W.785": null, "21W.786": null, "21W.790": null, "CMS.301": null, "CMS.307": null, "CMS.309": null, "CMS.314": null, "CMS.335": null, "CMS.336": null, "CMS.338": null, "CMS.608": null, "CMS.609": null, "CMS.611": null, "CMS.613": null, "CMS.617": null, "CMS.622": null, "CMS.627": null, "CMS.628": null, "CMS.631": null, "SP.360": null, "MAS.110": null, "STS.035": null, "STS.056": null, "STS.064": null, "STS.068": null, "WGS.190": null}; 2 | -------------------------------------------------------------------------------- /www/old_www/hass-h.js: -------------------------------------------------------------------------------- 1 | var hass_h = {"4.211": null, "4.250": null, "8.225": null, "9.48": null, "11.001": null, "11.013": null, "11.014": null, "11.015": null, "11.016": null, "11.026": null, "11.127": null, "11.139": null, "11.150": null, "11.151": null, "11.153": null, "17.007": null, "17.01": null, "17.021": null, "17.035": null, "17.04": null, "24.00": null, "24.01": null, "24.02": null, "24.03": null, "24.04": null, "24.05": null, "24.06": null, "24.08": null, "24.09": null, "24.111": null, "24.112": null, "24.115": null, "24.118": null, "24.120": null, "24.201": null, "24.211": null, "24.215": null, "24.221": null, "24.222": null, "24.230": null, "24.231": null, "24.235": null, "24.236": null, "24.241": null, "24.242": null, "24.243": null, "24.244": null, "24.245": null, "24.251": null, "24.253": null, "24.260": null, "24.280": null, "24.912": null, "24.916": null, "21A.143": null, "21G.011": null, "21G.019": null, "21G.022": null, "21G.027": null, "21G.030": null, "21G.036": null, "21G.038": null, "21G.039": null, "21G.040": null, "21G.043": null, "21G.044": null, "21G.045": null, "21G.046": null, "21G.052": null, "21G.054": null, "21G.055": null, "21G.057": null, "21G.058": null, "21G.059": null, "21G.063": null, "21G.064": null, "21G.065": null, "21G.067": null, "21G.068": null, "21G.070": null, "21G.074": null, "21G.075": null, "21G.077": null, "21G.078": null, "21G.085": null, "21G.101": null, "21G.102": null, "21G.103": null, "21G.104": null, "21G.105": null, "21G.106": null, "21G.107": null, "21G.108": null, "21G.109": null, "21G.110": null, "21G.113": null, "21G.120": null, "21G.142": null, "21G.143": null, "21G.190": null, "21G.192": null, "21G.193": null, "21G.194": null, "21G.195": null, "21G.196": null, "21G.199": null, "21G.220": null, "21G.221": null, "21G.222": null, "21G.223": null, "21G.226": null, "21G.228": null, "21G.233": null, "21G.301": null, "21G.302": null, "21G.303": null, "21G.304": null, "21G.308": null, "21G.310": null, "21G.311": null, "21G.312": null, "21G.315": null, "21G.320": null, "21G.321": null, "21G.322": null, "21G.325": null, "21G.341": null, "21G.346": null, "21G.347": null, "21G.401": null, "21G.402": null, "21G.403": null, "21G.404": null, "21G.405": null, "21G.410": null, "21G.412": null, "21G.414": null, "21G.415": null, "21G.416": null, "21G.417": null, "21G.418": null, "21G.420": null, "21G.501": null, "21G.502": null, "21G.503": null, "21G.504": null, "21G.505": null, "21G.506": null, "21G.590": null, "21G.591": null, "21G.592": null, "21G.593": null, "21G.596": null, "21G.597": null, "21G.601": null, "21G.611": null, "21G.612": null, "21G.613": null, "21G.614": null, "21G.618": null, "21G.700": null, "21G.701": null, "21G.702": null, "21G.703": null, "21G.704": null, "21G.711": null, "21G.712": null, "21G.713": null, "21G.714": null, "21G.715": null, "21G.716": null, "21G.717": null, "21G.731": null, "21G.735": null, "21G.736": null, "21G.738": null, "21G.739": null, "21G.740": null, "21G.782": null, "21G.801": null, "21G.802": null, "21G.803": null, "21G.804": null, "21G.820": null, "21G.880": null, "21G.901": null, "21G.902": null, "21G.903": null, "21G.904": null, "21H.001": null, "21H.007": null, "21H.009": null, "21H.101": null, "21H.102": null, "21H.106": null, "21H.107": null, "21H.108": null, "21H.130": null, "21H.132": null, "21H.133": null, "21H.141": null, "21H.151": null, "21H.152": null, "21H.154": null, "21H.155": null, "21H.160": null, "21H.161": null, "21H.171": null, "21H.172": null, "21H.181": null, "21H.201": null, "21H.209": null, "21H.211": null, "21H.214": null, "21H.217": null, "21H.218": null, "21H.220": null, "21H.226": null, "21H.228": null, "21H.229": null, "21H.230": null, "21H.237": null, "21H.238": null, "21H.239": null, "21H.240": null, "21H.241": null, "21H.242": null, "21H.244": null, "21H.253": null, "21H.262": null, "21H.273": null, "21H.285": null, "21H.315": null, "21H.320": null, "21H.321": null, "21H.322": null, "21H.331": null, "21H.333": null, "21H.336": null, "21H.343": null, "21H.350": null, "21H.351": null, "21H.354": null, "21H.357": null, "21H.358": null, "21H.365": null, "21H.382": null, "21H.390": null, "21L.000": null, "21L.001": null, "21L.002": null, "21L.003": null, "21L.004": null, "21L.006": null, "21L.007": null, "21L.008": null, "21L.009": null, "21L.010": null, "21L.012": null, "21L.013": null, "21L.014": null, "21L.015": null, "21L.017": null, "21L.018": null, "21L.019": null, "21L.020": null, "21L.021": null, "21L.022": null, "21L.048": null, "21L.430": null, "21L.431": null, "21L.432": null, "21L.433": null, "21L.434": null, "21L.435": null, "21L.449": null, "21L.450": null, "21L.451": null, "21L.455": null, "21L.458": null, "21L.460": null, "21L.471": null, "21L.473": null, "21L.475": null, "21L.485": null, "21L.487": null, "21L.488": null, "21L.490": null, "21L.501": null, "21L.504": null, "21L.512": null, "21L.590": null, "21L.601": null, "21L.616": null, "21L.617": null, "21L.638": null, "21L.639": null, "21L.640": null, "21L.701": null, "21L.702": null, "21L.703": null, "21L.704": null, "21L.705": null, "21L.706": null, "21L.707": null, "21L.709": null, "21L.715": null, "21M.013": null, "21M.703": null, "21W.011": null, "21W.012": null, "21W.013": null, "21W.014": null, "21W.015": null, "21W.016": null, "21W.021": null, "21W.022": null, "21W.026": null, "21W.031": null, "21W.031": null, "21W.032": null, "21W.032": null, "21W.034": null, "21W.034": null, "21W.035": null, "21W.035": null, "21W.036": null, "21W.041": null, "21W.042": null, "21W.735": null, "21W.736": null, "21W.737": null, "21W.739": null, "21W.741": null, "21W.742": null, "21W.745": null, "21W.746": null, "21W.747": null, "21W.760": null, "21W.761": null, "21W.775": null, "21W.777": null, "21W.778": null, "21W.787": null, "21W.788": null, "21W.789": null, "21W.791": null, "21W.792": null, "CMS.100": null, "CMS.300": null, "CMS.311": null, "CMS.313": null, "CMS.334": null, "CMS.350": null, "CMS.356": null, "CMS.360": null, "CMS.376": null, "CMS.400": null, "CMS.403": null, "CMS.405": null, "CMS.407": null, "CMS.590": null, "CMS.614": null, "CMS.615": null, "CMS.619": null, "CMS.621": null, "CMS.633": null, "CMS.701": null, "CMS.874": null, "CC.110": null, "CC.111": null, "CC.116": null, "CC.120": null, "ES.112": null, "ES.113": null, "STS.001": null, "STS.003": null, "STS.004": null, "STS.006": null, "STS.007": null, "STS.009": null, "STS.022": null, "STS.023": null, "STS.025": null, "STS.026": null, "STS.027": null, "STS.032": null, "STS.034": null, "STS.042": null, "STS.048": null, "STS.049": null, "STS.050": null, "STS.080": null, "STS.087": null, "STS.088": null, "STS.089": null, "WGS.101": null, "WGS.109": null, "WGS.110": null, "WGS.111": null, "WGS.115": null, "WGS.140": null, "WGS.141": null, "WGS.142": null, "WGS.145": null, "WGS.154": null, "WGS.161": null, "WGS.190": null, "WGS.220": null, "WGS.226": null, "WGS.231": null, "WGS.233": null, "WGS.234": null, "WGS.235": null, "WGS.236": null, "WGS.240": null, "WGS.242": null, "WGS.275": null, "WGS.301": null}; 2 | -------------------------------------------------------------------------------- /www/old_www/hass-s.js: -------------------------------------------------------------------------------- 1 | var hass_s = {"1.801": null, "3.094": null, "3.981": null, "3.982": null, "3.983": null, "3.985": null, "3.986": null, "3.987": null, "3.993": null, "5.24": null, "6.207": null, "6.805": null, "7.331": null, "9.00": null, "9.20": null, "9.56": null, "9.65": null, "9.85": null, "11.002": null, "11.003": null, "11.005": null, "11.006": null, "11.011": null, "11.021": null, "11.025": null, "11.027": null, "11.123": null, "11.124": null, "11.125": null, "11.129": null, "11.131": null, "11.140": null, "11.142": null, "11.147": null, "11.152": null, "11.161": null, "11.162": null, "11.163": null, "11.164": null, "11.165": null, "11.166": null, "12.011": null, "14.01": null, "14.02": null, "14.03": null, "14.04": null, "14.05": null, "14.06": null, "14.11": null, "14.12": null, "14.13": null, "14.15": null, "14.16": null, "14.18": null, "14.19": null, "14.20": null, "14.21": null, "14.26": null, "14.27": null, "14.41": null, "14.42": null, "14.43": null, "14.44": null, "14.54": null, "14.64": null, "14.70": null, "14.73": null, "14.74": null, "14.75": null, "15.031": null, "15.037": null, "17.03": null, "17.051": null, "17.115": null, "17.145": null, "17.181": null, "17.195": null, "17.20": null, "17.245": null, "17.249": null, "17.251": null, "17.261": null, "17.263": null, "17.265": null, "17.267": null, "17.269": null, "17.275": null, "17.28": null, "17.30": null, "17.303": null, "17.307": null, "17.309": null, "17.315": null, "17.317": null, "17.33": null, "17.391": null, "17.393": null, "17.395": null, "17.397": null, "17.40": null, "17.401": null, "17.407": null, "17.41": null, "17.411": null, "17.42": null, "17.433": null, "17.445": null, "17.447": null, "17.473": null, "17.483": null, "17.50": null, "17.509": null, "17.515": null, "17.517": null, "17.523": null, "17.53": null, "17.537": null, "17.55": null, "17.561": null, "17.565": null, "17.567": null, "17.569": null, "17.57": null, "17.571": null, "17.581": null, "17.591": null, "17.801": null, "17.811": null, "22.04": null, "24.900": null, "24.901": null, "24.902": null, "24.903": null, "24.904": null, "24.906": null, "24.907": null, "24.908": null, "24.910": null, "24.913": null, "24.914": null, "24.915": null, "24.918": null, "EC.701": null, "EC.702": null, "21A.00": null, "21A.01": null, "21A.101": null, "21A.103": null, "21A.104": null, "21A.111": null, "21A.120": null, "21A.130": null, "21A.140": null, "21A.141": null, "21A.150": null, "21A.155": null, "21A.156": null, "21A.157": null, "21A.200": null, "21A.203": null, "21A.300": null, "21A.301": null, "21A.302": null, "21A.303": null, "21A.304": null, "21A.305": null, "21A.306": null, "21A.307": null, "21A.308": null, "21A.310": null, "21A.331": null, "21A.400": null, "21A.410": null, "21A.411": null, "21A.415": null, "21A.442": null, "21A.455": null, "21A.461": null, "21A.500": null, "21A.501": null, "21A.502": null, "21A.503": null, "21A.504": null, "21A.505": null, "21A.506": null, "21A.801": null, "21A.802": null, "21A.852": null, "21G.024": null, "21G.047": null, "21G.048": null, "21G.053": null, "21G.084": null, "21G.086": null, "21H.134": null, "21H.157": null, "21H.185": null, "21H.213": null, "21H.227": null, "21H.245": null, "21H.260": null, "21H.319": null, "21H.380": null, "21H.381": null, "21H.383": null, "21H.385": null, "21W.768": null, "CMS.361": null, "CMS.362": null, "CMS.610": null, "CMS.616": null, "HST.431": null, "IDS.055": null, "STS.002": null, "STS.008": null, "STS.012": null, "STS.043": null, "STS.044": null, "STS.046": null, "STS.047": null, "STS.060": null, "STS.062": null, "STS.065": null, "STS.070": null, "STS.071": null, "STS.074": null, "STS.075": null, "STS.081": null, "STS.082": null, "STS.084": null, "STS.085": null, "STS.086": null, "WGS.125": null, "WGS.150": null, "WGS.151": null, "WGS.155": null, "WGS.170": null, "WGS.172": null, "WGS.175": null, "WGS.221": null, "WGS.222": null, "WGS.225": null, "WGS.228": null, "WGS.229": null, "WGS.270": null, "WGS.271": null, "WGS.274": null, "WGS.276": null}; 2 | -------------------------------------------------------------------------------- /www/old_www/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/edfan/firehose/daaba0d1eef8a9d363cc39efc17e9df6ff96b610/www/old_www/logo.png -------------------------------------------------------------------------------- /www/old_www/no-certs.html: -------------------------------------------------------------------------------- 1 | 2 | Firehose 3 | 4 | 5 | 6 | 7 |

MIT certificates are required; you can get them here.

8 |

If you're using Firefox and selected your certificate, refresh the page.

9 | 10 | -------------------------------------------------------------------------------- /www/old_www/script.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/edfan/firehose/daaba0d1eef8a9d363cc39efc17e9df6ff96b610/www/old_www/script.js -------------------------------------------------------------------------------- /www/old_www/stylesheet.css: -------------------------------------------------------------------------------- 1 | tfoot input { 2 | width: 100%; 3 | padding: 3px; 4 | box-sizing: border-box; 5 | } 6 | 7 | tfoot { 8 | display: table-header-group; 9 | } 10 | 11 | #header-div { 12 | display: block; 13 | width: 100%; 14 | height: 20px; 15 | } 16 | 17 | #info-div { 18 | font-family: 'Open Sans', sans-serif; 19 | font-size: 70%; 20 | width: 425px; 21 | margin: auto; 22 | text-align: center; 23 | } 24 | 25 | #info-div img { 26 | display: block; 27 | margin-left: auto; 28 | margin-right: auto; 29 | } 30 | 31 | #terms-show-div { 32 | font-family: 'Open Sans', sans-serif; 33 | margin: 0 auto 0 auto; 34 | left: 50%; 35 | text-align: center; 36 | line-height: 1; 37 | } 38 | 39 | #terms-div { 40 | font-family: 'Open Sans', sans-serif; 41 | margin: 0 auto 0 auto; 42 | left: 50%; 43 | text-align: center; 44 | line-height: 1; 45 | } 46 | 47 | #selector-div { 48 | font-family: 'Open Sans', sans-serif; 49 | margin: 0 auto 0 auto; 50 | left: 50%; 51 | text-align: center; 52 | line-height: 1; 53 | } 54 | 55 | #buttons-div { 56 | margin: auto; 57 | text-align: center; 58 | } 59 | 60 | #spacer-div { 61 | height: 0px; 62 | } 63 | 64 | #spacer2-div { 65 | height: 10px; 66 | } 67 | 68 | #spacer3-div { 69 | height: 8px; 70 | } 71 | 72 | .btn-primary, .btn-primary.focus { 73 | color: #ec5339; 74 | background-color: #fff; 75 | border-color: #ec5339; 76 | background-image: none; 77 | outline: 0; 78 | -webkit-box-shadow: none; 79 | box-shadow: none; 80 | } 81 | 82 | .btn-primary.active, .btn-primary.active.focus { 83 | color: #fff; 84 | background-color: #ec5339; 85 | border-color: #ec5339; 86 | background-image: none; 87 | outline: 0; 88 | -webkit-box-shadow: none; 89 | box-shadow: none; 90 | } 91 | 92 | .btn-primary:hover, .btn-primary.active:hover { 93 | color: #fff; 94 | background-color: #3794dc; 95 | border-color: #3794dc; 96 | } 97 | 98 | .btn-secondary, .btn-secondary.focus, 99 | .btn-secondary.active, .btn-secondary.active.focus { 100 | background-image: none; 101 | outline: 0 !important; 102 | -webkit-box-shadow: none; 103 | box-shadow: none; 104 | } 105 | 106 | #eval-table-div { 107 | font-family: 'PT Serif', serif; 108 | width: 100%; 109 | max-width: 1200px; 110 | margin: auto; 111 | } 112 | 113 | #eval-loading { 114 | font-family: 'Open Sans', sans-serif; 115 | margin: 50px; 116 | text-align: center; 117 | font-size: 120%; 118 | } 119 | 120 | .eval-subtable { 121 | table-layout: fixed; 122 | width: 100%; 123 | } 124 | -------------------------------------------------------------------------------- /www/privacy.html: -------------------------------------------------------------------------------- 1 | 2 | Firehose 3 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |

Firehose does not store any of your data outside of your browser. No data is ever transmitted upstream to the server.

16 | 17 |

Google Analytics is enabled, but you can disable it via any blocking method without impacting your use of the site.

18 | 19 |

Back to Firehose

20 | 21 | 22 | -------------------------------------------------------------------------------- /www/scroller.dataTables.min.css: -------------------------------------------------------------------------------- 1 | div.DTS{display:block !important}div.DTS tbody th,div.DTS tbody td{white-space:nowrap}div.DTS div.DTS_Loading{z-index:1}div.DTS div.dataTables_scrollBody{background:#ffffff}div.DTS div.dataTables_scrollBody table{z-index:2}div.DTS div.dataTables_paginate,div.DTS div.dataTables_length{display:none} 2 | -------------------------------------------------------------------------------- /www/stylesheet.css: -------------------------------------------------------------------------------- 1 | #beta-banner { 2 | width: 100%; 3 | text-align: center; 4 | padding: 1em 0; 5 | border-bottom: 1px solid #999; 6 | } 7 | 8 | #left-div { 9 | float: left; 10 | text-align: center; 11 | width: 53%; 12 | } 13 | 14 | #left-int-div { 15 | width: 91%; 16 | height: 710px; 17 | margin: 0 auto; 18 | } 19 | 20 | .fc-scroller { 21 | overflow: hidden !important; 22 | } 23 | 24 | #right-div { 25 | float: right; 26 | width: 47%; 27 | } 28 | 29 | #header-div { 30 | display: block; 31 | width: 100%; 32 | height: 20px; 33 | } 34 | 35 | #info-div { 36 | font-family: 'Open Sans', sans-serif; 37 | font-size: 90%; 38 | width: 450px; 39 | margin: 0 auto; 40 | text-align: center; 41 | line-height: 7px; 42 | } 43 | 44 | #info-line { 45 | margin-top: 5px; 46 | font-size: 80%; 47 | line-height: 1.2; 48 | } 49 | 50 | #info2-div { 51 | font-family: 'Open Sans', sans-serif; 52 | font-size: 70%; 53 | width: 450px; 54 | margin: auto; 55 | text-align: center; 56 | line-height: 7px; 57 | } 58 | 59 | #info3-p { 60 | font-family: 'Open Sans', sans-serif; 61 | font-size: 70%; 62 | line-height: 7px; 63 | margin: 7 auto; 64 | } 65 | 66 | #export-div { 67 | margin: 10 auto; 68 | font-size: 90%; 69 | } 70 | 71 | #sbuttons-div { 72 | margin: 0 auto 5 auto; 73 | } 74 | 75 | #adv-buttons-div { 76 | margin: 10 auto 0 auto; 77 | } 78 | 79 | #export-div span { 80 | color: #0275d8; 81 | cursor: pointer; 82 | } 83 | 84 | #export-div span:hover { 85 | text-decoration: underline; 86 | } 87 | 88 | #modal-textarea { 89 | font-family: monospace; 90 | resize: none; 91 | } 92 | 93 | #footer-div { 94 | margin: 0 auto 375 auto; 95 | font-size: 65%; 96 | color: #B0B0B0; 97 | } 98 | 99 | #footer-div a { 100 | color: inherit; 101 | } 102 | 103 | #js-img-sm { 104 | height: 35px; 105 | opacity: 0.1; 106 | position: relative; 107 | } 108 | 109 | #js-img-lg { 110 | height: 35px; 111 | opacity: 0.6; 112 | display: none; 113 | } 114 | 115 | #info-div img { 116 | display: block; 117 | margin: 9px auto; 118 | 119 | } 120 | 121 | #selector-div { 122 | font-family: 'Open Sans', sans-serif; 123 | margin: 0 auto 0 auto; 124 | left: 50%; 125 | text-align: center; 126 | line-height: 1; 127 | } 128 | 129 | #selected-div { 130 | margin: 12 auto 0 auto; 131 | text-align: left; 132 | } 133 | 134 | #expand-button{ 135 | color: #0275d8; 136 | cursor: pointer; 137 | font-size: 70%; 138 | position: relative; 139 | top: 5px; 140 | } 141 | 142 | #expand-button:hover { 143 | text-decoration: underline; 144 | } 145 | 146 | #warning-div, #warning2-div { 147 | margin: 0 auto 5 auto; 148 | font-size: 70%; 149 | } 150 | 151 | #warning3-div { 152 | margin: 5 auto; 153 | font-size: 70%; 154 | } 155 | 156 | #buttons-div { 157 | margin: 20 auto; 158 | text-align: center; 159 | } 160 | 161 | #spacer-div { 162 | height: 0px; 163 | } 164 | 165 | #spacer2-div { 166 | height: 0px; 167 | } 168 | 169 | #spacer3-div { 170 | height: 4px; 171 | } 172 | 173 | #class-input-label { 174 | font-size: 110%; 175 | } 176 | 177 | #units-div { 178 | margin: 0 auto 10 auto; 179 | } 180 | 181 | #activity-button { 182 | margin: 5 auto 5 auto; 183 | font-size: 70%; 184 | color: #0275d8; 185 | cursor: pointer; 186 | } 187 | 188 | #activity-button:hover { 189 | text-decoration: underline; 190 | } 191 | 192 | .link-span { 193 | color: #0275d8; 194 | cursor: pointer; 195 | } 196 | 197 | .link-span:hover { 198 | text-decoration: underline; 199 | } 200 | 201 | .time { 202 | width: 80px; 203 | margin: 0 auto 10; 204 | } 205 | 206 | .btn .btn:focus { 207 | margin: 0.1em; 208 | -webkit-transition: all 0.1s ease-out; 209 | -moz-transition: all 0.1s ease-out; 210 | transition: all 0.1s ease-out; 211 | } 212 | 213 | .btn:hover { 214 | box-shadow: 0px 0px 4px #808080; 215 | -webkit-transition: all 0.1s ease-out; 216 | -moz-transition: all 0.1s ease-out; 217 | transition: all 0.1s ease-out; 218 | cursor: pointer; 219 | transform: scale(1.05); 220 | } 221 | 222 | .btn-group { 223 | margin-bottom: -1px; 224 | } 225 | 226 | .btn-primary, .btn-primary.focus { 227 | color: #ec5339; 228 | background-color: transparent; 229 | border-color: #ec5339; 230 | background-image: none; 231 | outline: 0 !important; 232 | } 233 | 234 | .btn-primary.active, .btn-primary.active.focus { 235 | color: #fff; 236 | background-color: #ec5339; 237 | border-color: #ec5339; 238 | background-image: none; 239 | outline: 0; 240 | } 241 | 242 | .btn-primary:hover, .btn-primary.active:hover { 243 | color: #fff; 244 | background-color: #3794dc; 245 | border-color: #3794dc; 246 | } 247 | 248 | .btn-secondary, .btn-secondary.focus, 249 | .btn-secondary.active, .btn-secondary.active.focus { 250 | background-image: none; 251 | outline: 0 !important; 252 | -webkit-box-shadow: none; 253 | box-shadow: none; 254 | } 255 | 256 | #eval-table-div { 257 | font-family: 'PT Serif', serif; 258 | width: 100%; 259 | max-width: 1200px; 260 | margin: 5 auto 15 auto; 261 | cursor: pointer; 262 | scrollbar-width: 10px; 263 | } 264 | 265 | #eval-loading { 266 | font-family: 'Open Sans', sans-serif; 267 | margin: 50px; 268 | text-align: center; 269 | font-size: 120%; 270 | } 271 | 272 | .eval-subtable { 273 | table-layout: fixed; 274 | width: 100%; 275 | } 276 | 277 | #desc-div p { 278 | margin-bottom: 6px; 279 | } 280 | 281 | .lazyload-img { 282 | margin-top: -1px; 283 | } 284 | 285 | #class-name { 286 | font-weight: bold; 287 | line-height: 1; 288 | } 289 | 290 | #class-type { 291 | font-size: 80%; 292 | } 293 | 294 | #class-eval { 295 | font-size: 80%; 296 | } 297 | 298 | #class-hours-disclaimer { 299 | display: none; 300 | font-size: 80%; 301 | } 302 | 303 | #class-desc { 304 | font-size: 80%; 305 | line-height: 1.25; 306 | } 307 | 308 | #class-buttons-div { 309 | margin: 7 auto 7 auto; 310 | } 311 | 312 | #manual-button { 313 | font-size: 80%; 314 | color: #0275d8; 315 | cursor: pointer; 316 | } 317 | 318 | #manual-button:hover { 319 | text-decoration: underline; 320 | } 321 | 322 | #manual-div { 323 | display: flex; 324 | margin: 5 auto 10 auto; 325 | } 326 | 327 | #manual-div > div { 328 | flex: 1; 329 | } 330 | 331 | /* classes */ 332 | 333 | .fc-event-container a { 334 | padding: 0.1em; 335 | -webkit-transition: all 0.1s ease-out; 336 | -moz-transition: all 0.1s ease-out; 337 | transition: all 0.1s ease-out; 338 | } 339 | .fc-event-container a:hover { 340 | cursor: pointer; 341 | } 342 | 343 | .fc-unthemed .fc-content, .fc-unthemed .fc-divider, .fc-unthemed .fc-list-heading td, .fc-unthemed .fc-list-view, .fc-unthemed .fc-popover, .fc-unthemed .fc-row, .fc-unthemed tbody, .fc-unthemed td, .fc-unthemed th, .fc-unthemed thead { 344 | border-color: #d4d4d4; 345 | } 346 | 347 | .fc-time { 348 | font-size: 0.75em !important; 349 | } 350 | 351 | .fc-content { 352 | margin-top: -1px; 353 | } 354 | 355 | .fc-content .fc-title { 356 | display: block; 357 | font-size: 0.8em; 358 | } 359 | 360 | .fc-content .fc-title::first-line { 361 | font-size: 1.3em; 362 | } 363 | 364 | ::-webkit-input-placeholder { /* WebKit, Blink, Edge */ 365 | color: #909090; 366 | } 367 | :-moz-placeholder { /* Mozilla Firefox 4 to 18 */ 368 | color: #909090; 369 | opacity: 1; 370 | } 371 | ::-moz-placeholder { /* Mozilla Firefox 19+ */ 372 | color: #909090; 373 | opacity: 1; 374 | } 375 | :-ms-input-placeholder { /* Internet Explorer 10-11 */ 376 | color: #909090; 377 | } 378 | 379 | .dataTables_scrollBody { 380 | background-color: transparent !important; 381 | } 382 | 383 | .dark-mode-invert { 384 | filter: invert(87.45%); 385 | } 386 | 387 | .dark-mode-background { 388 | background-color: #202020; 389 | } 390 | 391 | .dark-mode-faint, .dark-mode-container td { 392 | border-color: #303030 !important; 393 | } 394 | 395 | .dark-mode-light, .dark-mode-container tr { 396 | color: #c8c5c3; 397 | } 398 | 399 | .dark-mode-container .odd, .dark-mode-container .even>.sorting_1 { 400 | background-color: #282828 !important; 401 | } 402 | 403 | .dark-mode-container .odd>.sorting_1 { 404 | background-color: #303030 !important; 405 | } 406 | 407 | .dark-mode-container .even { 408 | background-color: #202020 !important; 409 | } 410 | 411 | ::-webkit-scrollbar-corner { 412 | background-color: rgba(0, 0, 0, 0); 413 | } 414 | 415 | ::-webkit-scrollbar, ::-webkit-scrollbar-track { 416 | background-color: #F5F5F5; 417 | border-radius: 10px; 418 | width: 10px; 419 | height: 10px; 420 | } 421 | 422 | ::-webkit-scrollbar-thumb { 423 | border-radius: 10px; 424 | background-color: #c0c0c0; 425 | } 426 | 427 | .dark-mode-html ::-webkit-scrollbar, .dark-mode-html ::-webkit-scrollbar-track { 428 | background-color: #242424; 429 | } 430 | 431 | .dark-mode-html ::-webkit-scrollbar-thumb { 432 | background-color: #999999; 433 | } 434 | 435 | .dark-mode-html .ui-timepicker-list>* { 436 | background-color: #202020 !important; 437 | color: #c8c5c3 !important; 438 | } 439 | 440 | .dark-mode-html a, .dark-mode-html #activity-button, .dark-mode-html #expand-button, .dark-mode-html #export-div span, .dark-mode-html .link-span, .dark-mode-html #manual-button { 441 | color: #88bbff; 442 | } 443 | 444 | select { 445 | border-color: #767676; 446 | } 447 | 448 | /* text boxes */ 449 | 450 | input { 451 | padding: 0.2em; 452 | } 453 | 454 | input:focus { 455 | outline: none; 456 | } 457 | --------------------------------------------------------------------------------