├── .gitignore ├── Gruntfile.coffee ├── dist └── thucal2.user.js ├── exp ├── 20130223 initial prototype │ ├── _include.js │ ├── combine.html │ ├── combine.iced │ ├── combine.js │ ├── common.iced │ ├── common.js │ ├── google.ics │ ├── grid.html │ ├── ical │ ├── ical.iced │ ├── ical.js │ ├── jxmh.html │ ├── output.ics │ ├── output2.ics │ ├── parse_G.iced │ ├── parse_G.js │ ├── parse_L.iced │ ├── parse_L.js │ ├── thucal2.user.iced │ ├── thucal2.user.js │ └── week.coffee ├── 20130225 feat - week display │ ├── basic.ics │ ├── test.manual.ics │ ├── test.manual2.ics │ └── week only.ics ├── 20130225 grad test │ └── thucal2-grad-sample.tar.gz └── 20130228 abnormal L caoxz │ └── caoxz.html ├── lib ├── FileSaver.min.js ├── jquery-1.8.2.min.js ├── moment.js ├── moment.min.gm.js └── moment.min.js ├── package.json ├── readme.md ├── renren.md ├── screenshot ├── thucal2.png ├── thucal2_1.png ├── thucal2_2.png ├── thucal2_3'.png ├── thucal2_3.png └── thucal2_4.png └── src └── thucal2.iced /.gitignore: -------------------------------------------------------------------------------- 1 | # temp 2 | /result/ 3 | 4 | # grunt 5 | /node_modules 6 | #/dist 7 | /build 8 | -------------------------------------------------------------------------------- /Gruntfile.coffee: -------------------------------------------------------------------------------- 1 | module.exports = (grunt) -> 2 | 'use strict' 3 | 4 | grunt.initConfig 5 | pkg: grunt.file.readJSON('package.json') 6 | 7 | iced: 8 | options: 9 | runtime: 'inline' 10 | debug: 11 | files: [ 12 | expand: true 13 | cwd: 'src' 14 | src: ['*.iced'] 15 | dest: 'build' 16 | ext: '.js' 17 | ] 18 | 19 | concat: 20 | options: 21 | banner: 22 | ''' 23 | // Copyright (c) <%= grunt.template.today('yyyy') %>, <%= pkg.author.name %>. (MIT Licensed) 24 | // ==UserScript== 25 | // @name <%= pkg.name %> 26 | // @namespace http://github.com/smilekzs 27 | // @version <%= pkg.version %> 28 | // @description <%= pkg.description %> 29 | // @include *.cic.tsinghua.edu.cn/syxk.vsyxkKcapb.do* 30 | // @include *.cic.tsinghua.edu.cn/xkBks.vxkBksXkbBs.do* 31 | // @include *.cic.tsinghua.edu.cn/xkYjs.vxkYjsXkbBs.do* 32 | // ==/UserScript== 33 | 34 | //#include 35 | ''' 36 | dist: { 37 | src: ['lib/FileSaver.min.js', 'lib/jquery-1.8.2.min.js', 'lib/moment.min.gm.js', 'build/thucal2.js'] 38 | dest: 'dist/thucal2.user.js' 39 | } 40 | 41 | watch: 42 | iced: 43 | files: ['src/*.iced'] 44 | tasks: ['iced'] 45 | 46 | clean: 47 | build: ['build/*'] 48 | release: ['dist/*'] 49 | 50 | grunt.registerMultiTask 'iced', 'Compile IcedCoffeeScript files into JavaScript', -> 51 | path = require('path') 52 | options = @options( 53 | bare: false 54 | separator: grunt.util.linefeed 55 | ) 56 | grunt.fail.warn 'Experimental destination wildcards are no longer supported. please refer to README.' if options.basePath or options.flatten 57 | grunt.verbose.writeflags options, 'Options' 58 | @files.forEach (f) -> 59 | output = f.src.filter((filepath) -> 60 | if grunt.file.exists(filepath) 61 | true 62 | else 63 | grunt.log.warn 'Source file \'' + filepath + '\' not found.' 64 | false 65 | ).map((filepath) -> 66 | compileCoffee filepath, options 67 | ).join(grunt.util.normalizelf(options.separator)) 68 | if output.length < 1 69 | grunt.log.warn 'Destination not written because compiled files were empty.' 70 | else 71 | grunt.file.write f.dest, output 72 | grunt.log.writeln 'File ' + f.dest + ' created.' 73 | 74 | compileCoffee = (srcFile, options) -> 75 | options = grunt.util._.extend filename: srcFile, options 76 | srcCode = grunt.file.read srcFile 77 | try 78 | return require('iced-coffee-script').compile srcCode, options 79 | catch e 80 | grunt.log.error e 81 | grunt.fail.warn 'CoffeeScript failed to compile.' 82 | 83 | grunt.loadNpmTasks 'grunt-contrib-concat' 84 | grunt.loadNpmTasks 'grunt-contrib-clean' 85 | grunt.loadNpmTasks 'grunt-contrib-watch' 86 | 87 | grunt.registerTask 'debug', ['iced:debug'] 88 | grunt.registerTask 'dev', ['debug', 'watch'] 89 | grunt.registerTask 'release', ['debug', 'concat'] 90 | -------------------------------------------------------------------------------- /exp/20130223 initial prototype/combine.iced: -------------------------------------------------------------------------------- 1 | window.getOrigin=getOrigin=(Gr, L)-> 2 | lastDay=L[L.length-1] 3 | lastItems=lastDay.items 4 | lastItem=lastItems[lastItems.length-1] 5 | z=lastDay.ymd.day() 6 | 7 | maxW=0 8 | for p in [6..1] by -1 9 | for it in Gr[z][p] 10 | if it.name==lastItem.name && (w=it.week[it.week.length-1])>maxW 11 | maxW=w 12 | 13 | lastDay.ymd.clone().subtract(maxW-1, 'weeks').subtract(z-1, 'days') 14 | 15 | window.combine=combine=(Gr, L, origin)-> 16 | # re-map L to Lrel[day-since-origin] 17 | Lrel=[] 18 | for x in L 19 | Lrel[x.ymd.diff(origin, 'days')]=x.items 20 | 21 | # override G-side attributes with L-side equivalent (if available) 22 | for z in [1..7] by 1 23 | for p in [1..6] by 1 24 | for gi in Gr[z][p] 25 | w=gi.week 26 | w=w[w.length-1] 27 | rel=(w-1)*7+(z-1) 28 | if (bin=Lrel[rel]) then for li in bin 29 | if !li.matched && li.name==gi.name 30 | li.matched=true 31 | gi.beginT=li.beginT 32 | gi.endT=li.endT 33 | gi.loc=li.loc 34 | break 35 | #else debugger 36 | Gr 37 | 38 | $(document).ready(-> 39 | L=parse_L($('#Lspan'), parseTermId('2012-2013-2')) 40 | {Gr, Gl}=parse_G($('#Gspan')) 41 | 42 | window.L=L 43 | window.Gr=Gr 44 | window.Gl=Gl 45 | window.origin=origin=getOrigin(Gr, L) 46 | combine(Gr, L, origin) 47 | ) 48 | -------------------------------------------------------------------------------- /exp/20130223 initial prototype/combine.js: -------------------------------------------------------------------------------- 1 | // Generated by IcedCoffeeScript 1.4.0b 2 | (function() { 3 | var combine, getOrigin; 4 | 5 | window.getOrigin = getOrigin = function(Gr, L) { 6 | var it, lastDay, lastItem, lastItems, maxW, p, w, z, _i, _j, _len, _ref; 7 | lastDay = L[L.length - 1]; 8 | lastItems = lastDay.items; 9 | lastItem = lastItems[lastItems.length - 1]; 10 | z = lastDay.ymd.day(); 11 | maxW = 0; 12 | for (p = _i = 6; _i >= 1; p = _i += -1) { 13 | _ref = Gr[z][p]; 14 | for (_j = 0, _len = _ref.length; _j < _len; _j++) { 15 | it = _ref[_j]; 16 | if (it.name === lastItem.name && (w = it.week[it.week.length - 1]) > maxW) { 17 | maxW = w; 18 | } 19 | } 20 | } 21 | return lastDay.ymd.clone().subtract(maxW - 1, 'weeks').subtract(z - 1, 'days'); 22 | }; 23 | 24 | window.combine = combine = function(Gr, L, origin) { 25 | var Lrel, bin, gi, li, p, rel, w, x, z, _i, _j, _k, _l, _len, _len1, _len2, _m, _ref; 26 | Lrel = []; 27 | for (_i = 0, _len = L.length; _i < _len; _i++) { 28 | x = L[_i]; 29 | Lrel[x.ymd.diff(origin, 'days')] = x.items; 30 | } 31 | for (z = _j = 1; _j <= 7; z = _j += 1) { 32 | for (p = _k = 1; _k <= 6; p = _k += 1) { 33 | _ref = Gr[z][p]; 34 | for (_l = 0, _len1 = _ref.length; _l < _len1; _l++) { 35 | gi = _ref[_l]; 36 | w = gi.week; 37 | w = w[w.length - 1]; 38 | rel = (w - 1) * 7 + (z - 1); 39 | if ((bin = Lrel[rel])) { 40 | for (_m = 0, _len2 = bin.length; _m < _len2; _m++) { 41 | li = bin[_m]; 42 | if (!li.matched && li.name === gi.name) { 43 | li.matched = true; 44 | gi.beginT = li.beginT; 45 | gi.endT = li.endT; 46 | gi.loc = li.loc; 47 | break; 48 | } 49 | } 50 | } 51 | } 52 | } 53 | } 54 | return Gr; 55 | }; 56 | 57 | $(document).ready(function() { 58 | var Gl, Gr, L, origin, _ref; 59 | L = parse_L($('#Lspan'), parseTermId('2012-2013-2')); 60 | _ref = parse_G($('#Gspan')), Gr = _ref.Gr, Gl = _ref.Gl; 61 | window.L = L; 62 | window.Gr = Gr; 63 | window.Gl = Gl; 64 | window.origin = origin = getOrigin(Gr, L); 65 | return combine(Gr, L, origin); 66 | }); 67 | 68 | }).call(this); 69 | -------------------------------------------------------------------------------- /exp/20130223 initial prototype/common.iced: -------------------------------------------------------------------------------- 1 | window.buildArray=buildArray=(dims...)-> 2 | if !(dims?.length) then return null 3 | d=dims?.shift() 4 | if d.length? 5 | a=[] 6 | for i in d 7 | a[i]=buildArray(dims...) 8 | else 9 | a=new Array(d) 10 | for i in [0...d] by 1 11 | a[i]=buildArray(dims...) 12 | a 13 | 14 | window.cmp=cmp=(a, b)-> 15 | switch 16 | when ab then +1 18 | else 0 19 | 20 | window.inferYear=inferYear=(termIdP, m, d)-> 21 | md=moment().month(m-1).date(d) 22 | th=moment().month(5-1).date(1) 23 | if termIdP.termN==1 && md.isAfter th 24 | y=termIdP.beginY 25 | else 26 | y=termIdP.endY 27 | moment([y, m-1, d]) 28 | 29 | window.getTOffset=getTOffset=(t)->t.clone().diff(t.clone().startOf('day')) 30 | 31 | window.period=period=[ 32 | "00:00" # placeholder 33 | "08:00" #1 34 | "09:50" #2 35 | "13:30" #3 36 | "15:20" #4 37 | "17:05" #5 38 | "19:20" #6 39 | ].map((s)-> 40 | t=moment(s, 'HHmm') 41 | { 42 | beginT: getTOffset(t) 43 | endT : getTOffset(t.add(1, 'hours').add(35, 'minutes')) 44 | } 45 | ) 46 | 47 | window.parseTermId=parseTermId=(termId)-> 48 | if !(match=/(\d{4})-(\d{4})-(\d)/.exec termId)? then return null 49 | { 50 | beginY: parseInt match[1] 51 | endY : parseInt match[2] 52 | termN : parseInt match[3] 53 | } 54 | 55 | window.printTermId=(termIdP)-> 56 | termIdP.beginY+'-'+termIdP.endY+'-'+( 57 | switch termIdP.termN 58 | when 1 then '秋' 59 | when 2 then '春' 60 | else '不科学' 61 | ) 62 | 63 | window.parseTimeStr=parseTimeStr=(infoStr)-> 64 | if !(match=/时间(\d{1,2}:\d{1,2})-(\d{1,2}:\d{1,2})/.exec(infoStr))? then return null 65 | { 66 | beginT: getTOffset(moment(match[1], 'HHmm')) 67 | endT : getTOffset(moment(match[2], 'HHmm')) 68 | } 69 | 70 | window.parseWeekStr=parseWeekStr=(weekStr)-> 71 | if !(part=/(([\d,-]+)|全|前八|后八|单|双)周/.exec(weekStr)) then return null 72 | switch part[1].charAt(0) 73 | when '全' then return [1..16] 74 | when '前' then return [1..8] 75 | when '后' then return [9..16] 76 | when '单' then return (w for w in [1..16] by 2) 77 | when '双' then return (w for w in [2..16] by 2) 78 | else 79 | ret=[] 80 | for s in part[2].split(',') 81 | #isolated? 82 | if (match=/^\d+$/.exec(s))? 83 | ret.push(Number(match[0])) 84 | #range? 85 | if (match=/^(\d+)-(\d+)$/.exec(s))? 86 | ret.push(w) for w in [Number(match[1])..Number(match[2])] by 1 87 | ret 88 | -------------------------------------------------------------------------------- /exp/20130223 initial prototype/common.js: -------------------------------------------------------------------------------- 1 | // Generated by IcedCoffeeScript 1.4.0b 2 | (function() { 3 | var buildArray, cmp, getTOffset, inferYear, parseTermId, parseTimeStr, parseWeekStr, period, 4 | __slice = [].slice; 5 | 6 | window.buildArray = buildArray = function() { 7 | var a, d, dims, i, _i, _j, _len; 8 | dims = 1 <= arguments.length ? __slice.call(arguments, 0) : []; 9 | if (!(dims != null ? dims.length : void 0)) return null; 10 | d = dims != null ? dims.shift() : void 0; 11 | if (d.length != null) { 12 | a = []; 13 | for (_i = 0, _len = d.length; _i < _len; _i++) { 14 | i = d[_i]; 15 | a[i] = buildArray.apply(null, dims); 16 | } 17 | } else { 18 | a = new Array(d); 19 | for (i = _j = 0; _j < d; i = _j += 1) { 20 | a[i] = buildArray.apply(null, dims); 21 | } 22 | } 23 | return a; 24 | }; 25 | 26 | window.cmp = cmp = function(a, b) { 27 | switch (false) { 28 | case !(a < b): 29 | return -1; 30 | case !(a > b): 31 | return +1; 32 | default: 33 | return 0; 34 | } 35 | }; 36 | 37 | window.inferYear = inferYear = function(termIdP, m, d) { 38 | var md, th, y; 39 | md = moment().month(m - 1).date(d); 40 | th = moment().month(5 - 1).date(1); 41 | if (termIdP.termN === 1 && md.isAfter(th)) { 42 | y = termIdP.beginY; 43 | } else { 44 | y = termIdP.endY; 45 | } 46 | return moment([y, m - 1, d]); 47 | }; 48 | 49 | window.getTOffset = getTOffset = function(t) { 50 | return t.clone().diff(t.clone().startOf('day')); 51 | }; 52 | 53 | window.period = period = ["00:00", "08:00", "09:50", "13:30", "15:20", "17:05", "19:20"].map(function(s) { 54 | var t; 55 | t = moment(s, 'HHmm'); 56 | return { 57 | beginT: getTOffset(t), 58 | endT: getTOffset(t.add(1, 'hours').add(35, 'minutes')) 59 | }; 60 | }); 61 | 62 | window.parseTermId = parseTermId = function(termId) { 63 | var match; 64 | if ((match = /(\d{4})-(\d{4})-(\d)/.exec(termId)) == null) return null; 65 | return { 66 | beginY: parseInt(match[1]), 67 | endY: parseInt(match[2]), 68 | termN: parseInt(match[3]) 69 | }; 70 | }; 71 | 72 | window.printTermId = function(termIdP) { 73 | return termIdP.beginY + '-' + termIdP.endY + '-' + ((function() { 74 | switch (termIdP.termN) { 75 | case 1: 76 | return '秋'; 77 | case 2: 78 | return '春'; 79 | default: 80 | return '不科学'; 81 | } 82 | })()); 83 | }; 84 | 85 | window.parseTimeStr = parseTimeStr = function(infoStr) { 86 | var match; 87 | if ((match = /时间(\d{1,2}:\d{1,2})-(\d{1,2}:\d{1,2})/.exec(infoStr)) == null) { 88 | return null; 89 | } 90 | return { 91 | beginT: getTOffset(moment(match[1], 'HHmm')), 92 | endT: getTOffset(moment(match[2], 'HHmm')) 93 | }; 94 | }; 95 | 96 | window.parseWeekStr = parseWeekStr = function(weekStr) { 97 | var match, part, ret, s, w, _i, _j, _len, _ref, _ref1, _ref2; 98 | if (!(part = /(([\d,-]+)|全|前八|后八|单|双)周/.exec(weekStr))) return null; 99 | switch (part[1].charAt(0)) { 100 | case '全': 101 | return [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]; 102 | case '前': 103 | return [1, 2, 3, 4, 5, 6, 7, 8]; 104 | case '后': 105 | return [9, 10, 11, 12, 13, 14, 15, 16]; 106 | case '单': 107 | return (function() { 108 | var _i, _results; 109 | _results = []; 110 | for (w = _i = 1; _i <= 16; w = _i += 2) { 111 | _results.push(w); 112 | } 113 | return _results; 114 | })(); 115 | case '双': 116 | return (function() { 117 | var _i, _results; 118 | _results = []; 119 | for (w = _i = 2; _i <= 16; w = _i += 2) { 120 | _results.push(w); 121 | } 122 | return _results; 123 | })(); 124 | default: 125 | ret = []; 126 | _ref = part[2].split(','); 127 | for (_i = 0, _len = _ref.length; _i < _len; _i++) { 128 | s = _ref[_i]; 129 | if ((match = /^\d+$/.exec(s)) != null) ret.push(Number(match[0])); 130 | if ((match = /^(\d+)-(\d+)$/.exec(s)) != null) { 131 | for (w = _j = _ref1 = Number(match[1]), _ref2 = Number(match[2]); _j <= _ref2; w = _j += 1) { 132 | ret.push(w); 133 | } 134 | } 135 | } 136 | return ret; 137 | } 138 | }; 139 | 140 | }).call(this); 141 | -------------------------------------------------------------------------------- /exp/20130223 initial prototype/google.ics: -------------------------------------------------------------------------------- 1 | BEGIN:VCALENDAR 2 | PRODID:-//Google Inc//Google Calendar 70.9054//EN 3 | VERSION:2.0 4 | CALSCALE:GREGORIAN 5 | METHOD:PUBLISH 6 | X-WR-CALNAME:THUCAL:2012-2013秋[20120914T174549] 7 | X-WR-TIMEZONE:Asia/Shanghai 8 | BEGIN:VTIMEZONE 9 | TZID:Asia/Shanghai 10 | X-LIC-LOCATION:Asia/Shanghai 11 | BEGIN:STANDARD 12 | TZOFFSETFROM:+0800 13 | TZOFFSETTO:+0800 14 | TZNAME:CST 15 | DTSTART:19700101T000000 16 | END:STANDARD 17 | END:VTIMEZONE 18 | BEGIN:VEVENT 19 | DTSTART;TZID=Asia/Shanghai:20121228T133000 20 | DTEND;TZID=Asia/Shanghai:20121228T150500 21 | DTSTAMP:20130220T133938Z 22 | UID:125lgmm8nmvt2p9pbfchukkuq4@google.com 23 | RECURRENCE-ID;TZID=Asia/Shanghai:20121228T133000 24 | CREATED:20120914T094602Z 25 | DESCRIPTION:(王成;任选;全周;三教3315) 26 | LAST-MODIFIED:20121222T145300Z 27 | LOCATION:三教3315 28 | SEQUENCE:0 29 | STATUS:CONFIRMED 30 | SUMMARY:日语期末 31 | TRANSP:OPAQUE 32 | END:VEVENT 33 | BEGIN:VEVENT 34 | DTSTART;TZID=Asia/Shanghai:20121123T152000 35 | DTEND;TZID=Asia/Shanghai:20121123T165500 36 | DTSTAMP:20130220T133938Z 37 | UID:clpfni3h31un5i8g88sm7fjqu0@google.com 38 | RECURRENCE-ID;TZID=Asia/Shanghai:20121123T152000 39 | CREATED:20120914T094558Z 40 | DESCRIPTION:(唐庆玉;任选;单周;五教5202) 41 | LAST-MODIFIED:20121125T081444Z 42 | LOCATION:五教5202 43 | SEQUENCE:0 44 | STATUS:CONFIRMED 45 | SUMMARY:电工技术与电子技术(2) 46 | TRANSP:OPAQUE 47 | END:VEVENT 48 | BEGIN:VEVENT 49 | DTSTART;TZID=Asia/Shanghai:20121105T133000 50 | DTEND;TZID=Asia/Shanghai:20121105T150500 51 | DTSTAMP:20130220T133938Z 52 | UID:sbacghdnvah58d9o05fu9vqq9k@google.com 53 | RECURRENCE-ID;TZID=Asia/Shanghai:20121105T133000 54 | CREATED:20120914T094716Z 55 | DESCRIPTION:(朱全海;全周;综合馆西网球场) 56 | LAST-MODIFIED:20121030T142753Z 57 | LOCATION:综合馆西网球场 58 | SEQUENCE:0 59 | STATUS:CONFIRMED 60 | SUMMARY:三千米! 61 | TRANSP:OPAQUE 62 | END:VEVENT 63 | BEGIN:VEVENT 64 | DTSTART;TZID=Asia/Shanghai:20121101T080000 65 | DTEND;TZID=Asia/Shanghai:20121101T093500 66 | DTSTAMP:20130220T133938Z 67 | UID:of0dpksnlptv26k95hthi2pt1k@google.com 68 | RECURRENCE-ID;TZID=Asia/Shanghai:20121101T080000 69 | CREATED:20120914T094611Z 70 | DESCRIPTION:(耿华;任选;全周;六教6A016) 71 | LAST-MODIFIED:20121025T001229Z 72 | LOCATION:建馆报告厅 73 | SEQUENCE:0 74 | STATUS:CONFIRMED 75 | SUMMARY:!!模电期中!! 76 | TRANSP:OPAQUE 77 | END:VEVENT 78 | BEGIN:VEVENT 79 | DTSTART;TZID=Asia/Shanghai:20120911T170500 80 | DTEND;TZID=Asia/Shanghai:20120911T184000 81 | RRULE:FREQ=WEEKLY;COUNT=16 82 | EXDATE;TZID=Asia/Shanghai:20121225T170500 83 | EXDATE;TZID=Asia/Shanghai:20121211T170500 84 | EXDATE;TZID=Asia/Shanghai:20121204T170500 85 | EXDATE;TZID=Asia/Shanghai:20121120T170500 86 | EXDATE;TZID=Asia/Shanghai:20121113T170500 87 | EXDATE;TZID=Asia/Shanghai:20121030T170500 88 | EXDATE;TZID=Asia/Shanghai:20121016T170500 89 | EXDATE;TZID=Asia/Shanghai:20121002T170500 90 | EXDATE;TZID=Asia/Shanghai:20120925T170500 91 | EXDATE;TZID=Asia/Shanghai:20120918T170500 92 | EXDATE;TZID=Asia/Shanghai:20120911T170500 93 | DTSTAMP:20130220T133938Z 94 | UID:btfh1i1piteotvbvsau18e3ijs@google.com 95 | CREATED:20120914T094636Z 96 | DESCRIPTION:概率统计王晓峰25(第5\,7\,9\,12\,15周) 97 | LAST-MODIFIED:20121009T073835Z 98 | LOCATION:五教5201 99 | SEQUENCE:0 100 | STATUS:CONFIRMED 101 | SUMMARY:概率论与数理统计习题课 Lab[概率统计王晓峰25] 102 | TRANSP:OPAQUE 103 | END:VEVENT 104 | BEGIN:VEVENT 105 | DTSTART;TZID=Asia/Shanghai:20121007T230000 106 | DTEND;TZID=Asia/Shanghai:20121008T000000 107 | DTSTAMP:20130220T133938Z 108 | UID:2c1jqmq94dp7er7fe1l0m7s12c@google.com 109 | ATTENDEE;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=ACCEPTED;X-NUM-GUE 110 | STS=0:mailto:t8aj979iemp37m2sc2475u3us4@group.calendar.google.com 111 | RECURRENCE-ID;TZID=Asia/Shanghai:20121007T230000 112 | CREATED:20120925T102241Z 113 | DESCRIPTION: 114 | LAST-MODIFIED:20120925T102421Z 115 | LOCATION: 116 | SEQUENCE:0 117 | STATUS:CONFIRMED 118 | SUMMARY:熄灯! 119 | TRANSP:OPAQUE 120 | END:VEVENT 121 | BEGIN:VEVENT 122 | DTSTART;TZID=Asia/Shanghai:20121007T230000 123 | DTEND;TZID=Asia/Shanghai:20121008T000000 124 | RRULE:FREQ=WEEKLY;UNTIL=20121231T150000Z;BYDAY=SU,MO,TU,WE,TH 125 | EXDATE;TZID=Asia/Shanghai:20121231T230000 126 | EXDATE;TZID=Asia/Shanghai:20121230T230000 127 | DTSTAMP:20130220T133938Z 128 | UID:2c1jqmq94dp7er7fe1l0m7s12c@google.com 129 | ATTENDEE;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=ACCEPTED;X-NUM-GUE 130 | STS=0:mailto:t8aj979iemp37m2sc2475u3us4@group.calendar.google.com 131 | CREATED:20120925T102241Z 132 | DESCRIPTION: 133 | LAST-MODIFIED:20120925T102421Z 134 | LOCATION: 135 | SEQUENCE:0 136 | STATUS:CONFIRMED 137 | SUMMARY:熄灯! 138 | TRANSP:OPAQUE 139 | END:VEVENT 140 | BEGIN:VEVENT 141 | DTSTART;TZID=Asia/Shanghai:20120910T095000 142 | DTEND;TZID=Asia/Shanghai:20120910T112500 143 | RRULE:FREQ=WEEKLY;COUNT=16 144 | EXDATE;TZID=Asia/Shanghai:20121001T095000 145 | DTSTAMP:20130220T133938Z 146 | UID:bsbp9co8f9coqtd9kmc4jrqf5g@google.com 147 | CREATED:20120914T094723Z 148 | DESCRIPTION:(耿华;任选;全周;六教6A016) 149 | LAST-MODIFIED:20120914T094723Z 150 | LOCATION:六教6A016 151 | SEQUENCE:0 152 | STATUS:CONFIRMED 153 | SUMMARY:模拟电子技术基础 154 | TRANSP:OPAQUE 155 | END:VEVENT 156 | BEGIN:VEVENT 157 | DTSTART;TZID=Asia/Shanghai:20120910T133000 158 | DTEND;TZID=Asia/Shanghai:20120910T150500 159 | RRULE:FREQ=WEEKLY;COUNT=16 160 | EXDATE;TZID=Asia/Shanghai:20121001T133000 161 | DTSTAMP:20130220T133938Z 162 | UID:sbacghdnvah58d9o05fu9vqq9k@google.com 163 | CREATED:20120914T094716Z 164 | DESCRIPTION:(朱全海;全周;综合馆西网球场) 165 | LAST-MODIFIED:20120914T094716Z 166 | LOCATION:综合馆西网球场 167 | SEQUENCE:0 168 | STATUS:CONFIRMED 169 | SUMMARY:三年级男生网球 170 | TRANSP:OPAQUE 171 | END:VEVENT 172 | BEGIN:VEVENT 173 | DTSTART;TZID=Asia/Shanghai:20120910T152000 174 | DTEND;TZID=Asia/Shanghai:20120910T165500 175 | RRULE:FREQ=WEEKLY;COUNT=16 176 | EXDATE;TZID=Asia/Shanghai:20121001T152000 177 | DTSTAMP:20130220T133938Z 178 | UID:bp5f5srkc1astrdcm2k3n9mj70@google.com 179 | CREATED:20120914T094712Z 180 | DESCRIPTION:(吕建强;任选;全周;艺教中心212) 181 | LAST-MODIFIED:20120914T094712Z 182 | LOCATION:艺教中心212 183 | SEQUENCE:0 184 | STATUS:CONFIRMED 185 | SUMMARY:20世纪中国歌曲发展史 186 | TRANSP:OPAQUE 187 | END:VEVENT 188 | BEGIN:VEVENT 189 | DTSTART;TZID=Asia/Shanghai:20120911T080000 190 | DTEND;TZID=Asia/Shanghai:20120911T093500 191 | RRULE:FREQ=WEEKLY;COUNT=16 192 | EXDATE;TZID=Asia/Shanghai:20121127T080000 193 | EXDATE;TZID=Asia/Shanghai:20121225T080000 194 | EXDATE;TZID=Asia/Shanghai:20121106T080000 195 | EXDATE;TZID=Asia/Shanghai:20121016T080000 196 | EXDATE;TZID=Asia/Shanghai:20121002T080000 197 | EXDATE;TZID=Asia/Shanghai:20120918T080000 198 | EXDATE;TZID=Asia/Shanghai:20121218T080000 199 | EXDATE;TZID=Asia/Shanghai:20121211T080000 200 | EXDATE;TZID=Asia/Shanghai:20121204T080000 201 | EXDATE;TZID=Asia/Shanghai:20121113T080000 202 | EXDATE;TZID=Asia/Shanghai:20121030T080000 203 | EXDATE;TZID=Asia/Shanghai:20121023T080000 204 | EXDATE;TZID=Asia/Shanghai:20121009T080000 205 | EXDATE;TZID=Asia/Shanghai:20120925T080000 206 | EXDATE;TZID=Asia/Shanghai:20120911T080000 207 | DTSTAMP:20130220T133938Z 208 | UID:4gcm6qcjf3e1a5trsp2bl08288@google.com 209 | CREATED:20120914T094702Z 210 | DESCRIPTION:齿轮范成实验Ⅱ-17(精仪系馆3308室;第11周) 211 | LAST-MODIFIED:20120914T094703Z 212 | LOCATION:精仪系馆3308室 213 | SEQUENCE:0 214 | STATUS:CONFIRMED 215 | SUMMARY:机械设计基础A(2) Lab[齿轮范成实验Ⅱ-17] 216 | TRANSP:OPAQUE 217 | END:VEVENT 218 | BEGIN:VEVENT 219 | DTSTART;TZID=Asia/Shanghai:20120911T080000 220 | DTEND;TZID=Asia/Shanghai:20120911T084500 221 | RRULE:FREQ=WEEKLY;COUNT=16 222 | EXDATE;TZID=Asia/Shanghai:20121225T080000 223 | EXDATE;TZID=Asia/Shanghai:20120918T080000 224 | EXDATE;TZID=Asia/Shanghai:20121218T080000 225 | EXDATE;TZID=Asia/Shanghai:20121211T080000 226 | EXDATE;TZID=Asia/Shanghai:20121204T080000 227 | EXDATE;TZID=Asia/Shanghai:20121120T080000 228 | EXDATE;TZID=Asia/Shanghai:20121113T080000 229 | EXDATE;TZID=Asia/Shanghai:20121023T080000 230 | EXDATE;TZID=Asia/Shanghai:20121016T080000 231 | EXDATE;TZID=Asia/Shanghai:20121127T080000 232 | EXDATE;TZID=Asia/Shanghai:20121106T080000 233 | EXDATE;TZID=Asia/Shanghai:20121009T080000 234 | EXDATE;TZID=Asia/Shanghai:20121002T080000 235 | EXDATE;TZID=Asia/Shanghai:20120925T080000 236 | EXDATE;TZID=Asia/Shanghai:20120911T080000 237 | DTSTAMP:20130220T133938Z 238 | UID:q9hk96g1cskgt4ognf52nvshmk@google.com 239 | CREATED:20120914T094659Z 240 | DESCRIPTION:齿轮范成实验Ⅰ-01(精仪系馆3308;第8周;时间8:00-8:45) 241 | LAST-MODIFIED:20120914T094700Z 242 | LOCATION:精仪系馆3308 243 | SEQUENCE:0 244 | STATUS:CONFIRMED 245 | SUMMARY:机械设计基础A(2) Lab[齿轮范成实验Ⅰ-01] 246 | TRANSP:OPAQUE 247 | END:VEVENT 248 | BEGIN:VEVENT 249 | DTSTART;TZID=Asia/Shanghai:20120911T080000 250 | DTEND;TZID=Asia/Shanghai:20120911T093500 251 | RRULE:FREQ=WEEKLY;COUNT=16 252 | EXDATE;TZID=Asia/Shanghai:20121218T080000 253 | EXDATE;TZID=Asia/Shanghai:20121211T080000 254 | EXDATE;TZID=Asia/Shanghai:20121106T080000 255 | EXDATE;TZID=Asia/Shanghai:20121225T080000 256 | EXDATE;TZID=Asia/Shanghai:20121127T080000 257 | EXDATE;TZID=Asia/Shanghai:20121113T080000 258 | EXDATE;TZID=Asia/Shanghai:20121016T080000 259 | EXDATE;TZID=Asia/Shanghai:20121002T080000 260 | EXDATE;TZID=Asia/Shanghai:20121204T080000 261 | EXDATE;TZID=Asia/Shanghai:20121120T080000 262 | EXDATE;TZID=Asia/Shanghai:20121030T080000 263 | EXDATE;TZID=Asia/Shanghai:20121023T080000 264 | EXDATE;TZID=Asia/Shanghai:20120925T080000 265 | EXDATE;TZID=Asia/Shanghai:20120918T080000 266 | EXDATE;TZID=Asia/Shanghai:20120911T080000 267 | DTSTAMP:20130220T133938Z 268 | UID:u6kqaprojpeg9073sjldidpapc@google.com 269 | CREATED:20120914T094657Z 270 | DESCRIPTION:简图测绘01(精仪系馆3308室;第5周) 271 | LAST-MODIFIED:20120914T094657Z 272 | LOCATION:精仪系馆3308室 273 | SEQUENCE:0 274 | STATUS:CONFIRMED 275 | SUMMARY:机械设计基础A(2) Lab[简图测绘01] 276 | TRANSP:OPAQUE 277 | END:VEVENT 278 | BEGIN:VEVENT 279 | DTSTART;TZID=Asia/Shanghai:20120911T095000 280 | DTEND;TZID=Asia/Shanghai:20120911T121500 281 | RRULE:FREQ=WEEKLY;COUNT=16 282 | EXDATE;TZID=Asia/Shanghai:20121225T095000 283 | EXDATE;TZID=Asia/Shanghai:20121204T095000 284 | EXDATE;TZID=Asia/Shanghai:20121030T095000 285 | EXDATE;TZID=Asia/Shanghai:20121218T095000 286 | EXDATE;TZID=Asia/Shanghai:20121211T095000 287 | EXDATE;TZID=Asia/Shanghai:20121127T095000 288 | EXDATE;TZID=Asia/Shanghai:20121113T095000 289 | EXDATE;TZID=Asia/Shanghai:20121106T095000 290 | EXDATE;TZID=Asia/Shanghai:20121016T095000 291 | EXDATE;TZID=Asia/Shanghai:20121009T095000 292 | EXDATE;TZID=Asia/Shanghai:20120925T095000 293 | EXDATE;TZID=Asia/Shanghai:20121023T095000 294 | EXDATE;TZID=Asia/Shanghai:20121002T095000 295 | EXDATE;TZID=Asia/Shanghai:20120918T095000 296 | EXDATE;TZID=Asia/Shanghai:20120911T095000 297 | DTSTAMP:20130220T133938Z 298 | UID:5qoj1as395uufed9iadfj7t7ps@google.com 299 | CREATED:20120914T094649Z 300 | DESCRIPTION:齿轮范成实验Ⅱ-17(精仪系馆3308室;第11周) 301 | LAST-MODIFIED:20120914T094649Z 302 | LOCATION:精仪系馆3308室 303 | SEQUENCE:0 304 | STATUS:CONFIRMED 305 | SUMMARY:机械设计基础A(2) Lab[齿轮范成实验Ⅱ-17] 306 | TRANSP:OPAQUE 307 | END:VEVENT 308 | BEGIN:VEVENT 309 | DTSTART;TZID=Asia/Shanghai:20120911T133000 310 | DTEND;TZID=Asia/Shanghai:20120911T150500 311 | RRULE:FREQ=WEEKLY;COUNT=16 312 | EXDATE;TZID=Asia/Shanghai:20121002T133000 313 | DTSTAMP:20130220T133938Z 314 | UID:i3250hn90l03qke6o284pprvgc@google.com 315 | CREATED:20120914T094646Z 316 | DESCRIPTION:(王成;任选;全周;三教3315) 317 | LAST-MODIFIED:20120914T094646Z 318 | LOCATION:三教3315 319 | SEQUENCE:0 320 | STATUS:CONFIRMED 321 | SUMMARY:日语(第二外国语)(1) 322 | TRANSP:OPAQUE 323 | END:VEVENT 324 | BEGIN:VEVENT 325 | DTSTART;TZID=Asia/Shanghai:20120911T152000 326 | DTEND;TZID=Asia/Shanghai:20120911T165500 327 | RRULE:FREQ=WEEKLY;COUNT=16 328 | EXDATE;TZID=Asia/Shanghai:20121002T152000 329 | DTSTAMP:20130220T133938Z 330 | UID:om7n0eq9p3dnr3isa1q96oivdg@google.com 331 | CREATED:20120914T094639Z 332 | DESCRIPTION:(王朝坤;任选;全周;六教6A309) 333 | LAST-MODIFIED:20120914T094640Z 334 | LOCATION:六教6A309 335 | SEQUENCE:0 336 | STATUS:CONFIRMED 337 | SUMMARY:编译原理 338 | TRANSP:OPAQUE 339 | END:VEVENT 340 | BEGIN:VEVENT 341 | DTSTART;TZID=Asia/Shanghai:20120912T080000 342 | DTEND;TZID=Asia/Shanghai:20120912T093500 343 | RRULE:FREQ=WEEKLY;COUNT=16 344 | EXDATE;TZID=Asia/Shanghai:20121128T080000 345 | EXDATE;TZID=Asia/Shanghai:20121121T080000 346 | EXDATE;TZID=Asia/Shanghai:20121017T080000 347 | EXDATE;TZID=Asia/Shanghai:20121003T080000 348 | DTSTAMP:20130220T133938Z 349 | UID:fpq8suonugdgnob8fah2dkphu8@google.com 350 | CREATED:20120914T094634Z 351 | DESCRIPTION:(唐庆玉;任选;全周;五教5202) 352 | LAST-MODIFIED:20120914T094634Z 353 | LOCATION:五教5202 354 | SEQUENCE:0 355 | STATUS:CONFIRMED 356 | SUMMARY:电工技术与电子技术(2) 357 | TRANSP:OPAQUE 358 | END:VEVENT 359 | BEGIN:VEVENT 360 | DTSTART;TZID=Asia/Shanghai:20120912T095000 361 | DTEND;TZID=Asia/Shanghai:20120912T121500 362 | RRULE:FREQ=WEEKLY;COUNT=16 363 | EXDATE;TZID=Asia/Shanghai:20121003T095000 364 | DTSTAMP:20130220T133938Z 365 | UID:h3rl3a7k7kgbhpak8c5ukp2ck4@google.com 366 | CREATED:20120914T094631Z 367 | DESCRIPTION:(姜培学;必修;全周;五教5104) 368 | LAST-MODIFIED:20120914T094631Z 369 | LOCATION:五教5104 370 | SEQUENCE:0 371 | STATUS:CONFIRMED 372 | SUMMARY:传热学 373 | TRANSP:OPAQUE 374 | END:VEVENT 375 | BEGIN:VEVENT 376 | DTSTART;TZID=Asia/Shanghai:20120912T133000 377 | DTEND;TZID=Asia/Shanghai:20120912T154500 378 | RRULE:FREQ=WEEKLY;COUNT=16 379 | EXDATE;TZID=Asia/Shanghai:20121219T133000 380 | EXDATE;TZID=Asia/Shanghai:20121226T133000 381 | EXDATE;TZID=Asia/Shanghai:20121128T133000 382 | EXDATE;TZID=Asia/Shanghai:20121107T133000 383 | EXDATE;TZID=Asia/Shanghai:20121024T133000 384 | EXDATE;TZID=Asia/Shanghai:20120926T133000 385 | EXDATE;TZID=Asia/Shanghai:20121003T133000 386 | EXDATE;TZID=Asia/Shanghai:20120919T133000 387 | EXDATE;TZID=Asia/Shanghai:20120912T133000 388 | DTSTAMP:20130220T133938Z 389 | UID:2ck3ht44hj5d5qfjsumc050ifs@google.com 390 | CREATED:20120914T094627Z 391 | DESCRIPTION:7(西主搂3-222;3-324;3-326等;第5-6\,8\,10-11\,13-15周;时间13:30-15:45) 392 | LAST-MODIFIED:20120914T094627Z 393 | LOCATION:西主搂3-222;3-324;3-326等 394 | SEQUENCE:0 395 | STATUS:CONFIRMED 396 | SUMMARY:电工技术与电子技术(2) Lab[7] 397 | TRANSP:OPAQUE 398 | END:VEVENT 399 | BEGIN:VEVENT 400 | DTSTART;TZID=Asia/Shanghai:20120912T133000 401 | DTEND;TZID=Asia/Shanghai:20120912T154500 402 | RRULE:FREQ=WEEKLY;COUNT=16 403 | EXDATE;TZID=Asia/Shanghai:20121219T133000 404 | EXDATE;TZID=Asia/Shanghai:20121024T133000 405 | EXDATE;TZID=Asia/Shanghai:20120919T133000 406 | EXDATE;TZID=Asia/Shanghai:20121226T133000 407 | EXDATE;TZID=Asia/Shanghai:20121128T133000 408 | EXDATE;TZID=Asia/Shanghai:20121107T133000 409 | EXDATE;TZID=Asia/Shanghai:20121003T133000 410 | EXDATE;TZID=Asia/Shanghai:20120926T133000 411 | EXDATE;TZID=Asia/Shanghai:20120912T133000 412 | DTSTAMP:20130220T133938Z 413 | UID:bcd6ecngad32i8631trjnri2es@google.com 414 | CREATED:20120914T094623Z 415 | DESCRIPTION:7(西主搂3-222;3-324;3-326等;第5-6\,8\,10-11\,13-15周;时间13:30-15:45) 416 | LAST-MODIFIED:20120914T094624Z 417 | LOCATION:西主搂3-222;3-324;3-326等 418 | SEQUENCE:0 419 | STATUS:CONFIRMED 420 | SUMMARY:电工技术与电子技术(2) Lab[7] 421 | TRANSP:OPAQUE 422 | END:VEVENT 423 | BEGIN:VEVENT 424 | DTSTART;TZID=Asia/Shanghai:20120912T152000 425 | DTEND;TZID=Asia/Shanghai:20120912T160500 426 | RRULE:FREQ=WEEKLY;COUNT=16 427 | EXDATE;TZID=Asia/Shanghai:20121003T152000 428 | EXDATE;TZID=Asia/Shanghai:20120912T152000 429 | EXDATE;TZID=Asia/Shanghai:20121226T152000 430 | EXDATE;TZID=Asia/Shanghai:20121219T152000 431 | EXDATE;TZID=Asia/Shanghai:20121212T152000 432 | EXDATE;TZID=Asia/Shanghai:20121128T152000 433 | EXDATE;TZID=Asia/Shanghai:20121114T152000 434 | EXDATE;TZID=Asia/Shanghai:20121031T152000 435 | EXDATE;TZID=Asia/Shanghai:20121017T152000 436 | EXDATE;TZID=Asia/Shanghai:20121205T152000 437 | EXDATE;TZID=Asia/Shanghai:20121121T152000 438 | EXDATE;TZID=Asia/Shanghai:20121107T152000 439 | EXDATE;TZID=Asia/Shanghai:20121024T152000 440 | EXDATE;TZID=Asia/Shanghai:20121010T152000 441 | EXDATE;TZID=Asia/Shanghai:20120919T152000 442 | DTSTAMP:20130220T133938Z 443 | UID:v3lei5r77lvkqmk7m38v57qfds@google.com 444 | CREATED:20120914T094621Z 445 | DESCRIPTION:典型机构实例展示17(精仪系馆3308;第3周;时间15:20-16:05) 446 | LAST-MODIFIED:20120914T094621Z 447 | LOCATION:精仪系馆3308 448 | SEQUENCE:0 449 | STATUS:CONFIRMED 450 | SUMMARY:机械设计基础A(2) Lab[典型机构实例展示17] 451 | TRANSP:OPAQUE 452 | END:VEVENT 453 | BEGIN:VEVENT 454 | DTSTART;TZID=Asia/Shanghai:20120912T170500 455 | DTEND;TZID=Asia/Shanghai:20120912T184000 456 | RRULE:FREQ=WEEKLY;COUNT=16 457 | EXDATE;TZID=Asia/Shanghai:20121226T170500 458 | EXDATE;TZID=Asia/Shanghai:20121010T170500 459 | EXDATE;TZID=Asia/Shanghai:20121003T170500 460 | DTSTAMP:20130220T133938Z 461 | UID:ugjun833enph5e63ebpoqhl6vo@google.com 462 | CREATED:20120914T094618Z 463 | DESCRIPTION:(潘尚峰;任选;全周;精仪系系馆4303) 464 | LAST-MODIFIED:20120914T094618Z 465 | LOCATION:精仪系系馆4303 466 | SEQUENCE:0 467 | STATUS:CONFIRMED 468 | SUMMARY:液压传动与控制 469 | TRANSP:OPAQUE 470 | END:VEVENT 471 | BEGIN:VEVENT 472 | DTSTART;TZID=Asia/Shanghai:20120913T080000 473 | DTEND;TZID=Asia/Shanghai:20120913T093500 474 | RRULE:FREQ=WEEKLY;COUNT=16 475 | EXDATE;TZID=Asia/Shanghai:20121227T080000 476 | EXDATE;TZID=Asia/Shanghai:20121004T080000 477 | DTSTAMP:20130220T133938Z 478 | UID:of0dpksnlptv26k95hthi2pt1k@google.com 479 | CREATED:20120914T094611Z 480 | DESCRIPTION:(耿华;任选;全周;六教6A016) 481 | LAST-MODIFIED:20120914T094611Z 482 | LOCATION:六教6A016 483 | SEQUENCE:0 484 | STATUS:CONFIRMED 485 | SUMMARY:模拟电子技术基础 486 | TRANSP:OPAQUE 487 | END:VEVENT 488 | BEGIN:VEVENT 489 | DTSTART;TZID=Asia/Shanghai:20120913T095000 490 | DTEND;TZID=Asia/Shanghai:20120913T121500 491 | RRULE:FREQ=WEEKLY;COUNT=16 492 | EXDATE;TZID=Asia/Shanghai:20121115T095000 493 | EXDATE;TZID=Asia/Shanghai:20121004T095000 494 | DTSTAMP:20130220T133938Z 495 | UID:9f8hrohitt1rms5r4s2g99i5ds@google.com 496 | CREATED:20120914T094608Z 497 | DESCRIPTION:(郝智秀;必修;全周;六教6A201) 498 | LAST-MODIFIED:20120914T094608Z 499 | LOCATION:六教6A201 500 | SEQUENCE:0 501 | STATUS:CONFIRMED 502 | SUMMARY:机械设计基础A(2) 503 | TRANSP:OPAQUE 504 | END:VEVENT 505 | BEGIN:VEVENT 506 | DTSTART;TZID=Asia/Shanghai:20120914T095000 507 | DTEND;TZID=Asia/Shanghai:20120914T121500 508 | RRULE:FREQ=WEEKLY;COUNT=16 509 | EXDATE;TZID=Asia/Shanghai:20121005T095000 510 | DTSTAMP:20130220T133938Z 511 | UID:l62qo8s8a38bb6gn5t2tqfbaro@google.com 512 | CREATED:20120914T094605Z 513 | DESCRIPTION:(王晓峰;限选;全周;二教402) 514 | LAST-MODIFIED:20120914T094605Z 515 | LOCATION:二教402 516 | SEQUENCE:0 517 | STATUS:CONFIRMED 518 | SUMMARY:概率论与数理统计 519 | TRANSP:OPAQUE 520 | END:VEVENT 521 | BEGIN:VEVENT 522 | DTSTART;TZID=Asia/Shanghai:20120914T133000 523 | DTEND;TZID=Asia/Shanghai:20120914T150500 524 | RRULE:FREQ=WEEKLY;COUNT=16 525 | EXDATE;TZID=Asia/Shanghai:20121005T133000 526 | DTSTAMP:20130220T133938Z 527 | UID:125lgmm8nmvt2p9pbfchukkuq4@google.com 528 | CREATED:20120914T094602Z 529 | DESCRIPTION:(王成;任选;全周;三教3315) 530 | LAST-MODIFIED:20120914T094602Z 531 | LOCATION:三教3315 532 | SEQUENCE:0 533 | STATUS:CONFIRMED 534 | SUMMARY:日语(第二外国语)(1) 535 | TRANSP:OPAQUE 536 | END:VEVENT 537 | BEGIN:VEVENT 538 | DTSTART;TZID=Asia/Shanghai:20120914T152000 539 | DTEND;TZID=Asia/Shanghai:20120914T165500 540 | RRULE:FREQ=WEEKLY;COUNT=16 541 | EXDATE;TZID=Asia/Shanghai:20121228T152000 542 | EXDATE;TZID=Asia/Shanghai:20121214T152000 543 | EXDATE;TZID=Asia/Shanghai:20121130T152000 544 | EXDATE;TZID=Asia/Shanghai:20121116T152000 545 | EXDATE;TZID=Asia/Shanghai:20121102T152000 546 | EXDATE;TZID=Asia/Shanghai:20121019T152000 547 | EXDATE;TZID=Asia/Shanghai:20121005T152000 548 | EXDATE;TZID=Asia/Shanghai:20120921T152000 549 | DTSTAMP:20130220T133938Z 550 | UID:clpfni3h31un5i8g88sm7fjqu0@google.com 551 | CREATED:20120914T094558Z 552 | DESCRIPTION:(唐庆玉;任选;单周;五教5202) 553 | LAST-MODIFIED:20120914T094559Z 554 | LOCATION:五教5202 555 | SEQUENCE:0 556 | STATUS:CONFIRMED 557 | SUMMARY:电工技术与电子技术(2) 558 | TRANSP:OPAQUE 559 | END:VEVENT 560 | BEGIN:VEVENT 561 | DTSTART;TZID=Asia/Shanghai:20120915T133000 562 | DTEND;TZID=Asia/Shanghai:20120915T150000 563 | RRULE:FREQ=WEEKLY;COUNT=16 564 | EXDATE;TZID=Asia/Shanghai:20121215T133000 565 | EXDATE;TZID=Asia/Shanghai:20121013T133000 566 | EXDATE;TZID=Asia/Shanghai:20121222T133000 567 | EXDATE;TZID=Asia/Shanghai:20121208T133000 568 | EXDATE;TZID=Asia/Shanghai:20121006T133000 569 | EXDATE;TZID=Asia/Shanghai:20121229T133000 570 | EXDATE;TZID=Asia/Shanghai:20121201T133000 571 | EXDATE;TZID=Asia/Shanghai:20121124T133000 572 | EXDATE;TZID=Asia/Shanghai:20121110T133000 573 | EXDATE;TZID=Asia/Shanghai:20121103T133000 574 | EXDATE;TZID=Asia/Shanghai:20121027T133000 575 | EXDATE;TZID=Asia/Shanghai:20121020T133000 576 | EXDATE;TZID=Asia/Shanghai:20120922T133000 577 | EXDATE;TZID=Asia/Shanghai:20120929T133000 578 | EXDATE;TZID=Asia/Shanghai:20120915T133000 579 | DTSTAMP:20130220T133938Z 580 | UID:7d0kfgc5nmcnl24r8pjhcogjfk@google.com 581 | CREATED:20120914T094553Z 582 | DESCRIPTION:对流实验12(土木馆一楼西侧;第10周;时间13:30-15:00) 583 | LAST-MODIFIED:20120914T094555Z 584 | LOCATION:土木馆一楼西侧 585 | SEQUENCE:0 586 | STATUS:CONFIRMED 587 | SUMMARY:传热学 Lab[对流实验12] 588 | TRANSP:OPAQUE 589 | END:VEVENT 590 | BEGIN:VEVENT 591 | DTSTART;TZID=Asia/Shanghai:20120915T133000 592 | DTEND;TZID=Asia/Shanghai:20120915T150000 593 | RRULE:FREQ=WEEKLY;COUNT=16 594 | EXDATE;TZID=Asia/Shanghai:20121103T133000 595 | EXDATE;TZID=Asia/Shanghai:20121208T133000 596 | EXDATE;TZID=Asia/Shanghai:20121229T133000 597 | EXDATE;TZID=Asia/Shanghai:20121222T133000 598 | EXDATE;TZID=Asia/Shanghai:20121201T133000 599 | EXDATE;TZID=Asia/Shanghai:20121110T133000 600 | EXDATE;TZID=Asia/Shanghai:20121006T133000 601 | EXDATE;TZID=Asia/Shanghai:20120929T133000 602 | EXDATE;TZID=Asia/Shanghai:20121215T133000 603 | EXDATE;TZID=Asia/Shanghai:20121124T133000 604 | EXDATE;TZID=Asia/Shanghai:20121117T133000 605 | EXDATE;TZID=Asia/Shanghai:20121027T133000 606 | EXDATE;TZID=Asia/Shanghai:20121020T133000 607 | EXDATE;TZID=Asia/Shanghai:20120922T133000 608 | EXDATE;TZID=Asia/Shanghai:20120915T133000 609 | DTSTAMP:20130220T133938Z 610 | UID:86korap80h0kqct9ijbq4eodrk@google.com 611 | CREATED:20120914T094550Z 612 | DESCRIPTION:稳态实验12(土木馆一楼西侧;第5周;时间13:30-15:00) 613 | LAST-MODIFIED:20120914T094550Z 614 | LOCATION:土木馆一楼西侧 615 | SEQUENCE:0 616 | STATUS:CONFIRMED 617 | SUMMARY:传热学 Lab[稳态实验12] 618 | TRANSP:OPAQUE 619 | END:VEVENT 620 | END:VCALENDAR 621 | -------------------------------------------------------------------------------- /exp/20130223 initial prototype/grid.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 11 | 12 | 13 | 432 | 433 | 434 |
14 | 15 | 16 | 17 | 19 | 24 | 29 | 34 | 39 | 44 | 49 | 54 | 55 | 56 | 61 | 64 | 67 | 78 | 81 | 84 | 87 | 90 | 91 | 92 | 97 | 100 | 111 | 122 | 133 | 144 | 147 | 150 | 151 | 152 | 157 | 180 | 191 | 194 | 207 | 218 | 221 | 224 | 225 | 226 | 231 | 242 | 275 | 286 | 339 | 350 | 353 | 356 | 357 | 358 | 363 | 366 | 369 | 372 | 375 | 378 | 381 | 384 | 385 | 386 | 391 | 394 | 405 | 416 | 419 | 422 | 425 | 428 | 429 | 430 |
431 |
435 | 436 | 437 | -------------------------------------------------------------------------------- /exp/20130223 initial prototype/ical: -------------------------------------------------------------------------------- 1 | BEGIN:VCALENDAR 2 | PRODID:-//Google Inc//Google Calendar 70.9054//EN 3 | VERSION:2.0 4 | CALSCALE:GREGORIAN 5 | METHOD:PUBLISH 6 | X-WR-CALNAME:THUCAL:2012-2013秋[20120914T174549] 7 | X-WR-TIMEZONE:Asia/Shanghai 8 | BEGIN:VTIMEZONE 9 | TZID:Asia/Shanghai 10 | X-LIC-LOCATION:Asia/Shanghai 11 | BEGIN:STANDARD 12 | TZOFFSETFROM:+0800 13 | TZOFFSETTO:+0800 14 | TZNAME:CST 15 | DTSTART:19700101T000000 16 | END:STANDARD 17 | END:VTIMEZONE 18 | 19 | 20 | BEGIN:VEVENT 21 | DTSTART;TZID=Asia/Shanghai:20120914T152000 22 | DTEND;TZID=Asia/Shanghai:20120914T165500 23 | RRULE:FREQ=WEEKLY;COUNT=16 24 | EXDATE;TZID=Asia/Shanghai:20121228T152000 25 | EXDATE;TZID=Asia/Shanghai:20121214T152000 26 | EXDATE;TZID=Asia/Shanghai:20121130T152000 27 | EXDATE;TZID=Asia/Shanghai:20121116T152000 28 | EXDATE;TZID=Asia/Shanghai:20121102T152000 29 | EXDATE;TZID=Asia/Shanghai:20121019T152000 30 | EXDATE;TZID=Asia/Shanghai:20121005T152000 31 | EXDATE;TZID=Asia/Shanghai:20120921T152000 32 | DTSTAMP:20130220T133938Z 33 | UID:clpfni3h31un5i8g88sm7fjqu0@google.com 34 | CREATED:20120914T094558Z 35 | DESCRIPTION:(唐庆玉;任选;单周;五教5202) 36 | LAST-MODIFIED:20120914T094559Z 37 | LOCATION:五教5202 38 | SEQUENCE:0 39 | STATUS:CONFIRMED 40 | SUMMARY:电工技术与电子技术(2) 41 | TRANSP:OPAQUE 42 | END:VEVENT 43 | 44 | 45 | BEGIN:VEVENT 46 | DTSTART;TZID=Asia/Shanghai:20120911T170500 47 | DTEND;TZID=Asia/Shanghai:20120911T184000 48 | RRULE:FREQ=WEEKLY;COUNT=16 49 | EXDATE;TZID=Asia/Shanghai:20121225T170500 50 | EXDATE;TZID=Asia/Shanghai:20121211T170500 51 | EXDATE;TZID=Asia/Shanghai:20121204T170500 52 | EXDATE;TZID=Asia/Shanghai:20121120T170500 53 | EXDATE;TZID=Asia/Shanghai:20121113T170500 54 | EXDATE;TZID=Asia/Shanghai:20121030T170500 55 | EXDATE;TZID=Asia/Shanghai:20121016T170500 56 | EXDATE;TZID=Asia/Shanghai:20121002T170500 57 | EXDATE;TZID=Asia/Shanghai:20120925T170500 58 | EXDATE;TZID=Asia/Shanghai:20120918T170500 59 | EXDATE;TZID=Asia/Shanghai:20120911T170500 60 | DTSTAMP:20130220T133938Z 61 | UID:btfh1i1piteotvbvsau18e3ijs@google.com 62 | CREATED:20120914T094636Z 63 | DESCRIPTION:概率统计王晓峰25(第5\,7\,9\,12\,15周) 64 | LAST-MODIFIED:20121009T073835Z 65 | LOCATION:五教5201 66 | SEQUENCE:0 67 | STATUS:CONFIRMED 68 | SUMMARY:概率论与数理统计习题课 Lab[概率统计王晓峰25] 69 | TRANSP:OPAQUE 70 | END:VEVENT 71 | 72 | 73 | END:VCALENDAR 74 | -------------------------------------------------------------------------------- /exp/20130223 initial prototype/ical.iced: -------------------------------------------------------------------------------- 1 | ICAL_HEADER=""" 2 | BEGIN:VCALENDAR 3 | PRODID:-//smilekzs//thucal//EN 4 | VERSION:2.0 5 | CALSCALE:GREGORIAN 6 | METHOD:PUBLISH 7 | X-WR-CALNAME:THU:2012-2013-2 8 | X-WR-TIMEZONE:Asia/Shanghai 9 | BEGIN:VTIMEZONE 10 | TZID:Asia/Shanghai 11 | X-LIC-LOCATION:Asia/Shanghai 12 | BEGIN:STANDARD 13 | TZOFFSETFROM:+0800 14 | TZOFFSETTO:+0800 15 | TZNAME:CST 16 | DTSTART:19700101T000000 17 | END:STANDARD 18 | END:VTIMEZONE 19 | 20 | """ 21 | ICAL_FOOTER=""" 22 | END:VCALENDAR 23 | 24 | """ 25 | ICAL_EVENT=""" 26 | BEGIN:VEVENT 27 | SUMMARY: 28 | LOCATION: 29 | DESCRIPTION: 30 | DTSTART;TZID=Asia/Shanghai: 31 | DTEND;TZID=Asia/Shanghai: 32 | RRULE:FREQ=WEEKLY;COUNT=16 33 | 34 | SEQUENCE:0 35 | STATUS:CONFIRMED 36 | END:VEVENT 37 | 38 | """ 39 | ICAL_EX=""" 40 | EXDATE;TZID=Asia/Shanghai: 41 | 42 | """ 43 | window.ical=ical=new -> 44 | @escape=(s)-> 45 | s.replace(/,/g, '\\,') 46 | @template=(tmpl, obj)-> 47 | ret=tmpl 48 | for k, v of obj 49 | ret=ret.replace(RegExp('<'+k+'>'), v) 50 | ret 51 | @dateStr=(base, offset)-> 52 | base.clone().add(offset).format('YYYYMMDD[T]HHmmss') 53 | @nameStr=(gi)-> 54 | ret=gi.name 55 | if gi.labName 56 | ret+=' ['+gi.labName+']' 57 | ret 58 | @makeEx=(d1, gi)-> 59 | exclude=new Array(16+1) 60 | for i in [1..16] by 1 61 | exclude[i]=true 62 | for w in gi.week 63 | exclude[w]=false 64 | ret=[] 65 | for i in [1..16] by 1 66 | if exclude[i] 67 | ret.push @template ICAL_EX, 68 | date: @dateStr(d1.clone().add(i-1, 'weeks'), gi.beginT) 69 | ret.join('') 70 | @makeG=(G, origin)-> 71 | ret=[] 72 | for z in [1..7] by 1 73 | d1=origin.clone().add(z-1, 'days') 74 | for p in [1..6] by 1 75 | for gi in G[z][p] 76 | ret.push @template ICAL_EVENT, 77 | name : @escape @nameStr gi 78 | loc : @escape gi.loc 79 | desc : @escape gi.infoStr 80 | start : @dateStr(d1, gi.beginT) 81 | end : @dateStr(d1, gi.endT ) 82 | ex : @makeEx(d1, gi) 83 | ret.join('') 84 | @make=(Gr, Gl, origin)-> 85 | return ICAL_HEADER+@makeG(Gr, origin)+@makeG(Gl, origin)+ICAL_FOOTER 86 | this 87 | -------------------------------------------------------------------------------- /exp/20130223 initial prototype/ical.js: -------------------------------------------------------------------------------- 1 | // Generated by IcedCoffeeScript 1.4.0b 2 | (function() { 3 | var ICAL_EVENT, ICAL_EX, ICAL_FOOTER, ICAL_HEADER, ical; 4 | 5 | ICAL_HEADER = "BEGIN:VCALENDAR\nPRODID:-//smilekzs//thucal//EN\nVERSION:2.0\nCALSCALE:GREGORIAN\nMETHOD:PUBLISH\nX-WR-CALNAME:THU:2012-2013-2\nX-WR-TIMEZONE:Asia/Shanghai\nBEGIN:VTIMEZONE\nTZID:Asia/Shanghai\nX-LIC-LOCATION:Asia/Shanghai\nBEGIN:STANDARD\nTZOFFSETFROM:+0800\nTZOFFSETTO:+0800\nTZNAME:CST\nDTSTART:19700101T000000\nEND:STANDARD\nEND:VTIMEZONE\n"; 6 | 7 | ICAL_FOOTER = "END:VCALENDAR\n"; 8 | 9 | ICAL_EVENT = "BEGIN:VEVENT\nSUMMARY:\nLOCATION:\nDESCRIPTION:\nDTSTART;TZID=Asia/Shanghai:\nDTEND;TZID=Asia/Shanghai:\nRRULE:FREQ=WEEKLY;COUNT=16\n\nSEQUENCE:0\nSTATUS:CONFIRMED\nEND:VEVENT\n"; 10 | 11 | ICAL_EX = "EXDATE;TZID=Asia/Shanghai:\n"; 12 | 13 | window.ical = ical = new function() { 14 | this.escape = function(s) { 15 | return s.replace(/,/g, '\\,'); 16 | }; 17 | this.template = function(tmpl, obj) { 18 | var k, ret, v; 19 | ret = tmpl; 20 | for (k in obj) { 21 | v = obj[k]; 22 | ret = ret.replace(RegExp('<' + k + '>'), v); 23 | } 24 | return ret; 25 | }; 26 | this.dateStr = function(base, offset) { 27 | return base.clone().add(offset).format('YYYYMMDD[T]HHmmss'); 28 | }; 29 | this.nameStr = function(gi) { 30 | var ret; 31 | ret = gi.name; 32 | if (gi.labName) ret += ' [' + gi.labName + ']'; 33 | return ret; 34 | }; 35 | this.makeEx = function(d1, gi) { 36 | var exclude, i, ret, w, _i, _j, _k, _len, _ref; 37 | exclude = new Array(16 + 1); 38 | for (i = _i = 1; _i <= 16; i = _i += 1) { 39 | exclude[i] = true; 40 | } 41 | _ref = gi.week; 42 | for (_j = 0, _len = _ref.length; _j < _len; _j++) { 43 | w = _ref[_j]; 44 | exclude[w] = false; 45 | } 46 | ret = []; 47 | for (i = _k = 1; _k <= 16; i = _k += 1) { 48 | if (exclude[i]) { 49 | ret.push(this.template(ICAL_EX, { 50 | date: this.dateStr(d1.clone().add(i - 1, 'weeks'), gi.beginT) 51 | })); 52 | } 53 | } 54 | return ret.join(''); 55 | }; 56 | this.makeG = function(G, origin) { 57 | var d1, gi, p, ret, z, _i, _j, _k, _len, _ref; 58 | ret = []; 59 | for (z = _i = 1; _i <= 7; z = _i += 1) { 60 | d1 = origin.clone().add(z - 1, 'days'); 61 | for (p = _j = 1; _j <= 6; p = _j += 1) { 62 | _ref = G[z][p]; 63 | for (_k = 0, _len = _ref.length; _k < _len; _k++) { 64 | gi = _ref[_k]; 65 | ret.push(this.template(ICAL_EVENT, { 66 | name: this.escape(this.nameStr(gi)), 67 | loc: this.escape(gi.loc), 68 | desc: this.escape(gi.infoStr), 69 | start: this.dateStr(d1, gi.beginT), 70 | end: this.dateStr(d1, gi.endT), 71 | ex: this.makeEx(d1, gi) 72 | })); 73 | } 74 | } 75 | } 76 | return ret.join(''); 77 | }; 78 | this.make = function(Gr, Gl, origin) { 79 | return ICAL_HEADER + this.makeG(Gr, origin) + this.makeG(Gl, origin) + ICAL_FOOTER; 80 | }; 81 | return this; 82 | }; 83 | 84 | }).call(this); 85 | -------------------------------------------------------------------------------- /exp/20130223 initial prototype/output.ics: -------------------------------------------------------------------------------- 1 | BEGIN:VCALENDAR 2 | PRODID:-//smilekzs//thucal//EN 3 | VERSION:2.0 4 | CALSCALE:GREGORIAN 5 | METHOD:PUBLISH 6 | X-WR-CALNAME:THU:2012-2013-2 7 | X-WR-TIMEZONE:Asia/Shanghai 8 | BEGIN:VTIMEZONE 9 | TZID:Asia/Shanghai 10 | X-LIC-LOCATION:Asia/Shanghai 11 | BEGIN:STANDARD 12 | TZOFFSETFROM:+0800 13 | TZOFFSETTO:+0800 14 | TZNAME:CST 15 | DTSTART:19700101T000000 16 | END:STANDARD 17 | END:VTIMEZONE 18 | BEGIN:VEVENT 19 | SUMMARY:日语(第二外国语)(2) 20 | LOCATION:六教6A103 21 | DESCRIPTION:(隽雪艳;任选;全周;六教6A103) 22 | DTSTART;TZID=Asia/Shanghai:20130225T152000 23 | DTEND;TZID=Asia/Shanghai:20130225T165500 24 | RRULE:FREQ=WEEKLY;COUNT=16 25 | 26 | SEQUENCE:0 27 | STATUS:CONFIRMED 28 | END:VEVENT 29 | BEGIN:VEVENT 30 | SUMMARY:机械设计基础A(3) 31 | LOCATION:六教6A215 32 | DESCRIPTION:(刘向锋;必修;全周;六教6A215) 33 | DTSTART;TZID=Asia/Shanghai:20130226T095000 34 | DTEND;TZID=Asia/Shanghai:20130226T121500 35 | RRULE:FREQ=WEEKLY;COUNT=16 36 | 37 | SEQUENCE:0 38 | STATUS:CONFIRMED 39 | END:VEVENT 40 | BEGIN:VEVENT 41 | SUMMARY:留学申请实用写作 42 | LOCATION:六教6A111 43 | DESCRIPTION:(许建平;任选;前八周;六教6A111) 44 | DTSTART;TZID=Asia/Shanghai:20130226T133000 45 | DTEND;TZID=Asia/Shanghai:20130226T150500 46 | RRULE:FREQ=WEEKLY;COUNT=16 47 | EXDATE;TZID=Asia/Shanghai:20130423T133000 48 | EXDATE;TZID=Asia/Shanghai:20130430T133000 49 | EXDATE;TZID=Asia/Shanghai:20130507T133000 50 | EXDATE;TZID=Asia/Shanghai:20130514T133000 51 | EXDATE;TZID=Asia/Shanghai:20130521T133000 52 | EXDATE;TZID=Asia/Shanghai:20130528T133000 53 | EXDATE;TZID=Asia/Shanghai:20130604T133000 54 | EXDATE;TZID=Asia/Shanghai:20130611T133000 55 | 56 | SEQUENCE:0 57 | STATUS:CONFIRMED 58 | END:VEVENT 59 | BEGIN:VEVENT 60 | SUMMARY:自动化中的气动技术 61 | LOCATION:科技楼3317 62 | DESCRIPTION:(张锡文;任选;全周;科技楼3317) 63 | DTSTART;TZID=Asia/Shanghai:20130226T192000 64 | DTEND;TZID=Asia/Shanghai:20130226T205500 65 | RRULE:FREQ=WEEKLY;COUNT=16 66 | 67 | SEQUENCE:0 68 | STATUS:CONFIRMED 69 | END:VEVENT 70 | BEGIN:VEVENT 71 | SUMMARY:光学工程基础 72 | LOCATION:六教6A118 73 | DESCRIPTION:(朱钧;必修;全周;六教6A118) 74 | DTSTART;TZID=Asia/Shanghai:20130227T080000 75 | DTEND;TZID=Asia/Shanghai:20130227T093500 76 | RRULE:FREQ=WEEKLY;COUNT=16 77 | 78 | SEQUENCE:0 79 | STATUS:CONFIRMED 80 | END:VEVENT 81 | BEGIN:VEVENT 82 | SUMMARY:制造工程基础 83 | LOCATION:六教6A115 84 | DESCRIPTION:(吴丹;必修;全周;六教6A115) 85 | DTSTART;TZID=Asia/Shanghai:20130227T095000 86 | DTEND;TZID=Asia/Shanghai:20130227T121500 87 | RRULE:FREQ=WEEKLY;COUNT=16 88 | 89 | SEQUENCE:0 90 | STATUS:CONFIRMED 91 | END:VEVENT 92 | BEGIN:VEVENT 93 | SUMMARY:日语(第二外国语)(2) 94 | LOCATION:六教6A103 95 | DESCRIPTION:(隽雪艳;任选;全周;六教6A103) 96 | DTSTART;TZID=Asia/Shanghai:20130227T152000 97 | DTEND;TZID=Asia/Shanghai:20130227T165500 98 | RRULE:FREQ=WEEKLY;COUNT=16 99 | 100 | SEQUENCE:0 101 | STATUS:CONFIRMED 102 | END:VEVENT 103 | BEGIN:VEVENT 104 | SUMMARY:并行计算基础 105 | LOCATION:东主楼9区224室 106 | DESCRIPTION:(薛巍;任选;全周;东主楼9区224室) 107 | DTSTART;TZID=Asia/Shanghai:20130227T192000 108 | DTEND;TZID=Asia/Shanghai:20130227T205500 109 | RRULE:FREQ=WEEKLY;COUNT=16 110 | 111 | SEQUENCE:0 112 | STATUS:CONFIRMED 113 | END:VEVENT 114 | BEGIN:VEVENT 115 | SUMMARY:控制工程基础 116 | LOCATION:六教6A213 117 | DESCRIPTION:(董景新;必修;1-13周;六教6A213) 118 | DTSTART;TZID=Asia/Shanghai:20130228T095000 119 | DTEND;TZID=Asia/Shanghai:20130228T121500 120 | RRULE:FREQ=WEEKLY;COUNT=16 121 | EXDATE;TZID=Asia/Shanghai:20130530T095000 122 | EXDATE;TZID=Asia/Shanghai:20130606T095000 123 | EXDATE;TZID=Asia/Shanghai:20130613T095000 124 | 125 | SEQUENCE:0 126 | STATUS:CONFIRMED 127 | END:VEVENT 128 | BEGIN:VEVENT 129 | SUMMARY:测试与检测技术基础 130 | LOCATION:五教5102 131 | DESCRIPTION:(王伯雄;必修;全周;五教5102) 132 | DTSTART;TZID=Asia/Shanghai:20130301T095000 133 | DTEND;TZID=Asia/Shanghai:20130301T121500 134 | RRULE:FREQ=WEEKLY;COUNT=16 135 | 136 | SEQUENCE:0 137 | STATUS:CONFIRMED 138 | END:VEVENT 139 | BEGIN:VEVENT 140 | SUMMARY:电磁兼容设计 141 | LOCATION:六教6A103 142 | DESCRIPTION:(倪建平;任选;全周;六教6A103) 143 | DTSTART;TZID=Asia/Shanghai:20130301T133000 144 | DTEND;TZID=Asia/Shanghai:20130301T150500 145 | RRULE:FREQ=WEEKLY;COUNT=16 146 | 147 | SEQUENCE:0 148 | STATUS:CONFIRMED 149 | END:VEVENT 150 | BEGIN:VEVENT 151 | SUMMARY:三年级男生网球 152 | LOCATION:紫荆网球场 153 | DESCRIPTION:(刘铁一;全周;紫荆网球场) 154 | DTSTART;TZID=Asia/Shanghai:20130301T152000 155 | DTEND;TZID=Asia/Shanghai:20130301T165500 156 | RRULE:FREQ=WEEKLY;COUNT=16 157 | 158 | SEQUENCE:0 159 | STATUS:CONFIRMED 160 | END:VEVENT 161 | BEGIN:VEVENT 162 | SUMMARY:制造工程基础 [F1] 163 | LOCATION:精仪系馆一层CIMS大厅 164 | DESCRIPTION:F1(精仪系馆一层CIMS大厅;第8周;时间13:00-14:30) 165 | DTSTART;TZID=Asia/Shanghai:20130225T130000 166 | DTEND;TZID=Asia/Shanghai:20130225T143000 167 | RRULE:FREQ=WEEKLY;COUNT=16 168 | EXDATE;TZID=Asia/Shanghai:20130225T130000 169 | EXDATE;TZID=Asia/Shanghai:20130304T130000 170 | EXDATE;TZID=Asia/Shanghai:20130311T130000 171 | EXDATE;TZID=Asia/Shanghai:20130318T130000 172 | EXDATE;TZID=Asia/Shanghai:20130325T130000 173 | EXDATE;TZID=Asia/Shanghai:20130401T130000 174 | EXDATE;TZID=Asia/Shanghai:20130408T130000 175 | EXDATE;TZID=Asia/Shanghai:20130422T130000 176 | EXDATE;TZID=Asia/Shanghai:20130429T130000 177 | EXDATE;TZID=Asia/Shanghai:20130506T130000 178 | EXDATE;TZID=Asia/Shanghai:20130513T130000 179 | EXDATE;TZID=Asia/Shanghai:20130520T130000 180 | EXDATE;TZID=Asia/Shanghai:20130527T130000 181 | EXDATE;TZID=Asia/Shanghai:20130603T130000 182 | EXDATE;TZID=Asia/Shanghai:20130610T130000 183 | 184 | SEQUENCE:0 185 | STATUS:CONFIRMED 186 | END:VEVENT 187 | BEGIN:VEVENT 188 | SUMMARY:制造工程基础 [S1] 189 | LOCATION:精仪系馆一层CIMS大厅 190 | DESCRIPTION:S1(精仪系馆一层CIMS大厅;第12周;时间13:00-15:00) 191 | DTSTART;TZID=Asia/Shanghai:20130225T130000 192 | DTEND;TZID=Asia/Shanghai:20130225T150000 193 | RRULE:FREQ=WEEKLY;COUNT=16 194 | EXDATE;TZID=Asia/Shanghai:20130225T130000 195 | EXDATE;TZID=Asia/Shanghai:20130304T130000 196 | EXDATE;TZID=Asia/Shanghai:20130311T130000 197 | EXDATE;TZID=Asia/Shanghai:20130318T130000 198 | EXDATE;TZID=Asia/Shanghai:20130325T130000 199 | EXDATE;TZID=Asia/Shanghai:20130401T130000 200 | EXDATE;TZID=Asia/Shanghai:20130408T130000 201 | EXDATE;TZID=Asia/Shanghai:20130415T130000 202 | EXDATE;TZID=Asia/Shanghai:20130422T130000 203 | EXDATE;TZID=Asia/Shanghai:20130429T130000 204 | EXDATE;TZID=Asia/Shanghai:20130506T130000 205 | EXDATE;TZID=Asia/Shanghai:20130520T130000 206 | EXDATE;TZID=Asia/Shanghai:20130527T130000 207 | EXDATE;TZID=Asia/Shanghai:20130603T130000 208 | EXDATE;TZID=Asia/Shanghai:20130610T130000 209 | 210 | SEQUENCE:0 211 | STATUS:CONFIRMED 212 | END:VEVENT 213 | BEGIN:VEVENT 214 | SUMMARY:控制工程基础 [04] 215 | LOCATION:精仪系馆4101 216 | DESCRIPTION:04(精仪系馆4101;第13周;时间14:00-17:00) 217 | DTSTART;TZID=Asia/Shanghai:20130226T140000 218 | DTEND;TZID=Asia/Shanghai:20130226T170000 219 | RRULE:FREQ=WEEKLY;COUNT=16 220 | EXDATE;TZID=Asia/Shanghai:20130226T140000 221 | EXDATE;TZID=Asia/Shanghai:20130305T140000 222 | EXDATE;TZID=Asia/Shanghai:20130312T140000 223 | EXDATE;TZID=Asia/Shanghai:20130319T140000 224 | EXDATE;TZID=Asia/Shanghai:20130326T140000 225 | EXDATE;TZID=Asia/Shanghai:20130402T140000 226 | EXDATE;TZID=Asia/Shanghai:20130409T140000 227 | EXDATE;TZID=Asia/Shanghai:20130416T140000 228 | EXDATE;TZID=Asia/Shanghai:20130423T140000 229 | EXDATE;TZID=Asia/Shanghai:20130430T140000 230 | EXDATE;TZID=Asia/Shanghai:20130507T140000 231 | EXDATE;TZID=Asia/Shanghai:20130514T140000 232 | EXDATE;TZID=Asia/Shanghai:20130528T140000 233 | EXDATE;TZID=Asia/Shanghai:20130604T140000 234 | EXDATE;TZID=Asia/Shanghai:20130611T140000 235 | 236 | SEQUENCE:0 237 | STATUS:CONFIRMED 238 | END:VEVENT 239 | BEGIN:VEVENT 240 | SUMMARY:实验室科研探究 (I) [(集群计算机与科学计算)05_24] 241 | LOCATION:FIT楼2-D03 242 | DESCRIPTION:(集群计算机与科学计算)05_24( FIT楼2-D03;第5周) 243 | DTSTART;TZID=Asia/Shanghai:20130226T152000 244 | DTEND;TZID=Asia/Shanghai:20130226T165500 245 | RRULE:FREQ=WEEKLY;COUNT=16 246 | EXDATE;TZID=Asia/Shanghai:20130226T152000 247 | EXDATE;TZID=Asia/Shanghai:20130305T152000 248 | EXDATE;TZID=Asia/Shanghai:20130312T152000 249 | EXDATE;TZID=Asia/Shanghai:20130319T152000 250 | EXDATE;TZID=Asia/Shanghai:20130402T152000 251 | EXDATE;TZID=Asia/Shanghai:20130409T152000 252 | EXDATE;TZID=Asia/Shanghai:20130416T152000 253 | EXDATE;TZID=Asia/Shanghai:20130423T152000 254 | EXDATE;TZID=Asia/Shanghai:20130430T152000 255 | EXDATE;TZID=Asia/Shanghai:20130507T152000 256 | EXDATE;TZID=Asia/Shanghai:20130514T152000 257 | EXDATE;TZID=Asia/Shanghai:20130521T152000 258 | EXDATE;TZID=Asia/Shanghai:20130528T152000 259 | EXDATE;TZID=Asia/Shanghai:20130604T152000 260 | EXDATE;TZID=Asia/Shanghai:20130611T152000 261 | 262 | SEQUENCE:0 263 | STATUS:CONFIRMED 264 | END:VEVENT 265 | BEGIN:VEVENT 266 | SUMMARY:实验室科研探究 (I) [(室内外光环境设计)07_24] 267 | LOCATION:美院照明与色彩实验室 268 | DESCRIPTION:(室内外光环境设计)07_24( 美院照明与色彩实验室;第7周) 269 | DTSTART;TZID=Asia/Shanghai:20130226T152000 270 | DTEND;TZID=Asia/Shanghai:20130226T165500 271 | RRULE:FREQ=WEEKLY;COUNT=16 272 | EXDATE;TZID=Asia/Shanghai:20130226T152000 273 | EXDATE;TZID=Asia/Shanghai:20130305T152000 274 | EXDATE;TZID=Asia/Shanghai:20130312T152000 275 | EXDATE;TZID=Asia/Shanghai:20130319T152000 276 | EXDATE;TZID=Asia/Shanghai:20130326T152000 277 | EXDATE;TZID=Asia/Shanghai:20130402T152000 278 | EXDATE;TZID=Asia/Shanghai:20130416T152000 279 | EXDATE;TZID=Asia/Shanghai:20130423T152000 280 | EXDATE;TZID=Asia/Shanghai:20130430T152000 281 | EXDATE;TZID=Asia/Shanghai:20130507T152000 282 | EXDATE;TZID=Asia/Shanghai:20130514T152000 283 | EXDATE;TZID=Asia/Shanghai:20130521T152000 284 | EXDATE;TZID=Asia/Shanghai:20130528T152000 285 | EXDATE;TZID=Asia/Shanghai:20130604T152000 286 | EXDATE;TZID=Asia/Shanghai:20130611T152000 287 | 288 | SEQUENCE:0 289 | STATUS:CONFIRMED 290 | END:VEVENT 291 | BEGIN:VEVENT 292 | SUMMARY:实验室科研探究 (I) [(无处不在的液压系统)04_43] 293 | LOCATION:训练中心实习车间铣磨教室 294 | DESCRIPTION:(无处不在的液压系统)04_43( 训练中心实习车间铣磨教室;第4周) 295 | DTSTART;TZID=Asia/Shanghai:20130228T133000 296 | DTEND;TZID=Asia/Shanghai:20130228T150500 297 | RRULE:FREQ=WEEKLY;COUNT=16 298 | EXDATE;TZID=Asia/Shanghai:20130228T133000 299 | EXDATE;TZID=Asia/Shanghai:20130307T133000 300 | EXDATE;TZID=Asia/Shanghai:20130314T133000 301 | EXDATE;TZID=Asia/Shanghai:20130328T133000 302 | EXDATE;TZID=Asia/Shanghai:20130404T133000 303 | EXDATE;TZID=Asia/Shanghai:20130411T133000 304 | EXDATE;TZID=Asia/Shanghai:20130418T133000 305 | EXDATE;TZID=Asia/Shanghai:20130425T133000 306 | EXDATE;TZID=Asia/Shanghai:20130502T133000 307 | EXDATE;TZID=Asia/Shanghai:20130509T133000 308 | EXDATE;TZID=Asia/Shanghai:20130516T133000 309 | EXDATE;TZID=Asia/Shanghai:20130523T133000 310 | EXDATE;TZID=Asia/Shanghai:20130530T133000 311 | EXDATE;TZID=Asia/Shanghai:20130606T133000 312 | EXDATE;TZID=Asia/Shanghai:20130613T133000 313 | 314 | SEQUENCE:0 315 | STATUS:CONFIRMED 316 | END:VEVENT 317 | BEGIN:VEVENT 318 | SUMMARY:实验室科研探究 (I) [(概论课)02_44] 319 | LOCATION:建筑馆报告厅或看网络学堂通知 320 | DESCRIPTION:(概论课)02_44( 建筑馆报告厅或看网络学堂通知;第2周) 321 | DTSTART;TZID=Asia/Shanghai:20130228T152000 322 | DTEND;TZID=Asia/Shanghai:20130228T165500 323 | RRULE:FREQ=WEEKLY;COUNT=16 324 | EXDATE;TZID=Asia/Shanghai:20130228T152000 325 | EXDATE;TZID=Asia/Shanghai:20130314T152000 326 | EXDATE;TZID=Asia/Shanghai:20130321T152000 327 | EXDATE;TZID=Asia/Shanghai:20130328T152000 328 | EXDATE;TZID=Asia/Shanghai:20130404T152000 329 | EXDATE;TZID=Asia/Shanghai:20130411T152000 330 | EXDATE;TZID=Asia/Shanghai:20130418T152000 331 | EXDATE;TZID=Asia/Shanghai:20130425T152000 332 | EXDATE;TZID=Asia/Shanghai:20130502T152000 333 | EXDATE;TZID=Asia/Shanghai:20130509T152000 334 | EXDATE;TZID=Asia/Shanghai:20130516T152000 335 | EXDATE;TZID=Asia/Shanghai:20130523T152000 336 | EXDATE;TZID=Asia/Shanghai:20130530T152000 337 | EXDATE;TZID=Asia/Shanghai:20130606T152000 338 | EXDATE;TZID=Asia/Shanghai:20130613T152000 339 | 340 | SEQUENCE:0 341 | STATUS:CONFIRMED 342 | END:VEVENT 343 | BEGIN:VEVENT 344 | SUMMARY:实验室科研探究 (I) [(人机系统仿真)08_44] 345 | LOCATION:舜德楼南529 346 | DESCRIPTION:(人机系统仿真)08_44( 舜德楼南529;第8周) 347 | DTSTART;TZID=Asia/Shanghai:20130228T152000 348 | DTEND;TZID=Asia/Shanghai:20130228T165500 349 | RRULE:FREQ=WEEKLY;COUNT=16 350 | EXDATE;TZID=Asia/Shanghai:20130228T152000 351 | EXDATE;TZID=Asia/Shanghai:20130307T152000 352 | EXDATE;TZID=Asia/Shanghai:20130314T152000 353 | EXDATE;TZID=Asia/Shanghai:20130321T152000 354 | EXDATE;TZID=Asia/Shanghai:20130328T152000 355 | EXDATE;TZID=Asia/Shanghai:20130404T152000 356 | EXDATE;TZID=Asia/Shanghai:20130411T152000 357 | EXDATE;TZID=Asia/Shanghai:20130425T152000 358 | EXDATE;TZID=Asia/Shanghai:20130502T152000 359 | EXDATE;TZID=Asia/Shanghai:20130509T152000 360 | EXDATE;TZID=Asia/Shanghai:20130516T152000 361 | EXDATE;TZID=Asia/Shanghai:20130523T152000 362 | EXDATE;TZID=Asia/Shanghai:20130530T152000 363 | EXDATE;TZID=Asia/Shanghai:20130606T152000 364 | EXDATE;TZID=Asia/Shanghai:20130613T152000 365 | 366 | SEQUENCE:0 367 | STATUS:CONFIRMED 368 | END:VEVENT 369 | BEGIN:VEVENT 370 | SUMMARY:实验室科研探究 (I) [(仿人机器人技术暨RoboCup机器人足球赛)05_44] 371 | LOCATION:精仪系馆2501 372 | DESCRIPTION:(仿人机器人技术暨RoboCup机器人足球赛)05_44( 精仪系馆2501;第5周) 373 | DTSTART;TZID=Asia/Shanghai:20130228T152000 374 | DTEND;TZID=Asia/Shanghai:20130228T165500 375 | RRULE:FREQ=WEEKLY;COUNT=16 376 | EXDATE;TZID=Asia/Shanghai:20130228T152000 377 | EXDATE;TZID=Asia/Shanghai:20130307T152000 378 | EXDATE;TZID=Asia/Shanghai:20130314T152000 379 | EXDATE;TZID=Asia/Shanghai:20130321T152000 380 | EXDATE;TZID=Asia/Shanghai:20130404T152000 381 | EXDATE;TZID=Asia/Shanghai:20130411T152000 382 | EXDATE;TZID=Asia/Shanghai:20130418T152000 383 | EXDATE;TZID=Asia/Shanghai:20130425T152000 384 | EXDATE;TZID=Asia/Shanghai:20130502T152000 385 | EXDATE;TZID=Asia/Shanghai:20130509T152000 386 | EXDATE;TZID=Asia/Shanghai:20130516T152000 387 | EXDATE;TZID=Asia/Shanghai:20130523T152000 388 | EXDATE;TZID=Asia/Shanghai:20130530T152000 389 | EXDATE;TZID=Asia/Shanghai:20130606T152000 390 | EXDATE;TZID=Asia/Shanghai:20130613T152000 391 | 392 | SEQUENCE:0 393 | STATUS:CONFIRMED 394 | END:VEVENT 395 | BEGIN:VEVENT 396 | SUMMARY:实验室科研探究 (I) [(电力电子变换与电机控制)04_44] 397 | LOCATION:西主楼1区101室 398 | DESCRIPTION:(电力电子变换与电机控制)04_44( 西主楼1区101室;第4周) 399 | DTSTART;TZID=Asia/Shanghai:20130228T152000 400 | DTEND;TZID=Asia/Shanghai:20130228T165500 401 | RRULE:FREQ=WEEKLY;COUNT=16 402 | EXDATE;TZID=Asia/Shanghai:20130228T152000 403 | EXDATE;TZID=Asia/Shanghai:20130307T152000 404 | EXDATE;TZID=Asia/Shanghai:20130314T152000 405 | EXDATE;TZID=Asia/Shanghai:20130328T152000 406 | EXDATE;TZID=Asia/Shanghai:20130404T152000 407 | EXDATE;TZID=Asia/Shanghai:20130411T152000 408 | EXDATE;TZID=Asia/Shanghai:20130418T152000 409 | EXDATE;TZID=Asia/Shanghai:20130425T152000 410 | EXDATE;TZID=Asia/Shanghai:20130502T152000 411 | EXDATE;TZID=Asia/Shanghai:20130509T152000 412 | EXDATE;TZID=Asia/Shanghai:20130516T152000 413 | EXDATE;TZID=Asia/Shanghai:20130523T152000 414 | EXDATE;TZID=Asia/Shanghai:20130530T152000 415 | EXDATE;TZID=Asia/Shanghai:20130606T152000 416 | EXDATE;TZID=Asia/Shanghai:20130613T152000 417 | 418 | SEQUENCE:0 419 | STATUS:CONFIRMED 420 | END:VEVENT 421 | BEGIN:VEVENT 422 | SUMMARY:实验室科研探究 (I) [(照猫画猫——制造业的反求技术)07_44] 423 | LOCATION:训练中心快速成型实验室 424 | DESCRIPTION:(照猫画猫——制造业的反求技术)07_44( 训练中心快速成型实验室;第7周) 425 | DTSTART;TZID=Asia/Shanghai:20130228T152000 426 | DTEND;TZID=Asia/Shanghai:20130228T165500 427 | RRULE:FREQ=WEEKLY;COUNT=16 428 | EXDATE;TZID=Asia/Shanghai:20130228T152000 429 | EXDATE;TZID=Asia/Shanghai:20130307T152000 430 | EXDATE;TZID=Asia/Shanghai:20130314T152000 431 | EXDATE;TZID=Asia/Shanghai:20130321T152000 432 | EXDATE;TZID=Asia/Shanghai:20130328T152000 433 | EXDATE;TZID=Asia/Shanghai:20130404T152000 434 | EXDATE;TZID=Asia/Shanghai:20130418T152000 435 | EXDATE;TZID=Asia/Shanghai:20130425T152000 436 | EXDATE;TZID=Asia/Shanghai:20130502T152000 437 | EXDATE;TZID=Asia/Shanghai:20130509T152000 438 | EXDATE;TZID=Asia/Shanghai:20130516T152000 439 | EXDATE;TZID=Asia/Shanghai:20130523T152000 440 | EXDATE;TZID=Asia/Shanghai:20130530T152000 441 | EXDATE;TZID=Asia/Shanghai:20130606T152000 442 | EXDATE;TZID=Asia/Shanghai:20130613T152000 443 | 444 | SEQUENCE:0 445 | STATUS:CONFIRMED 446 | END:VEVENT 447 | END:VCALENDAR 448 | 449 | -------------------------------------------------------------------------------- /exp/20130223 initial prototype/output2.ics: -------------------------------------------------------------------------------- 1 | BEGIN:VCALENDAR 2 | PRODID:-//smilekzs//thucal//EN 3 | VERSION:2.0 4 | CALSCALE:GREGORIAN 5 | METHOD:PUBLISH 6 | X-WR-CALNAME:THU:2012-2013-2 7 | X-WR-TIMEZONE:Asia/Shanghai 8 | BEGIN:VTIMEZONE 9 | TZID:Asia/Shanghai 10 | X-LIC-LOCATION:Asia/Shanghai 11 | BEGIN:STANDARD 12 | TZOFFSETFROM:+0800 13 | TZOFFSETTO:+0800 14 | TZNAME:CST 15 | DTSTART:19700101T000000 16 | END:STANDARD 17 | END:VTIMEZONE 18 | BEGIN:VEVENT 19 | SUMMARY:日语(第二外国语)(2) 20 | LOCATION:六教6A103 21 | DESCRIPTION:(隽雪艳;任选;全周;六教6A103) 22 | DTSTART;TZID=Asia/Shanghai:20130225T152000 23 | DTEND;TZID=Asia/Shanghai:20130225T165500 24 | RRULE:FREQ=WEEKLY;COUNT=16 25 | 26 | SEQUENCE:0 27 | STATUS:CONFIRMED 28 | END:VEVENT 29 | BEGIN:VEVENT 30 | SUMMARY:机械设计基础A(3) 31 | LOCATION:六教6A215 32 | DESCRIPTION:(刘向锋;必修;全周;六教6A215) 33 | DTSTART;TZID=Asia/Shanghai:20130226T095000 34 | DTEND;TZID=Asia/Shanghai:20130226T121500 35 | RRULE:FREQ=WEEKLY;COUNT=16 36 | 37 | SEQUENCE:0 38 | STATUS:CONFIRMED 39 | END:VEVENT 40 | BEGIN:VEVENT 41 | SUMMARY:留学申请实用写作 42 | LOCATION:六教6A111 43 | DESCRIPTION:(许建平;任选;前八周;六教6A111) 44 | DTSTART;TZID=Asia/Shanghai:20130226T133000 45 | DTEND;TZID=Asia/Shanghai:20130226T150500 46 | RRULE:FREQ=WEEKLY;COUNT=16 47 | EXDATE;TZID=Asia/Shanghai:20130423T133000 48 | EXDATE;TZID=Asia/Shanghai:20130430T133000 49 | EXDATE;TZID=Asia/Shanghai:20130507T133000 50 | EXDATE;TZID=Asia/Shanghai:20130514T133000 51 | EXDATE;TZID=Asia/Shanghai:20130521T133000 52 | EXDATE;TZID=Asia/Shanghai:20130528T133000 53 | EXDATE;TZID=Asia/Shanghai:20130604T133000 54 | EXDATE;TZID=Asia/Shanghai:20130611T133000 55 | 56 | SEQUENCE:0 57 | STATUS:CONFIRMED 58 | END:VEVENT 59 | BEGIN:VEVENT 60 | SUMMARY:自动化中的气动技术 61 | LOCATION:科技楼3317 62 | DESCRIPTION:(张锡文;任选;全周;科技楼3317) 63 | DTSTART;TZID=Asia/Shanghai:20130226T192000 64 | DTEND;TZID=Asia/Shanghai:20130226T205500 65 | RRULE:FREQ=WEEKLY;COUNT=16 66 | 67 | SEQUENCE:0 68 | STATUS:CONFIRMED 69 | END:VEVENT 70 | BEGIN:VEVENT 71 | SUMMARY:光学工程基础 72 | LOCATION:六教6A118 73 | DESCRIPTION:(朱钧;必修;全周;六教6A118) 74 | DTSTART;TZID=Asia/Shanghai:20130227T080000 75 | DTEND;TZID=Asia/Shanghai:20130227T093500 76 | RRULE:FREQ=WEEKLY;COUNT=16 77 | 78 | SEQUENCE:0 79 | STATUS:CONFIRMED 80 | END:VEVENT 81 | BEGIN:VEVENT 82 | SUMMARY:制造工程基础 83 | LOCATION:六教6A115 84 | DESCRIPTION:(吴丹;必修;全周;六教6A115) 85 | DTSTART;TZID=Asia/Shanghai:20130227T095000 86 | DTEND;TZID=Asia/Shanghai:20130227T121500 87 | RRULE:FREQ=WEEKLY;COUNT=16 88 | 89 | SEQUENCE:0 90 | STATUS:CONFIRMED 91 | END:VEVENT 92 | BEGIN:VEVENT 93 | SUMMARY:日语(第二外国语)(2) 94 | LOCATION:六教6A103 95 | DESCRIPTION:(隽雪艳;任选;全周;六教6A103) 96 | DTSTART;TZID=Asia/Shanghai:20130227T152000 97 | DTEND;TZID=Asia/Shanghai:20130227T165500 98 | RRULE:FREQ=WEEKLY;COUNT=16 99 | 100 | SEQUENCE:0 101 | STATUS:CONFIRMED 102 | END:VEVENT 103 | BEGIN:VEVENT 104 | SUMMARY:并行计算基础 105 | LOCATION:东主楼9区224室 106 | DESCRIPTION:(薛巍;任选;全周;东主楼9区224室) 107 | DTSTART;TZID=Asia/Shanghai:20130227T192000 108 | DTEND;TZID=Asia/Shanghai:20130227T205500 109 | RRULE:FREQ=WEEKLY;COUNT=16 110 | 111 | SEQUENCE:0 112 | STATUS:CONFIRMED 113 | END:VEVENT 114 | BEGIN:VEVENT 115 | SUMMARY:控制工程基础 116 | LOCATION:六教6A213 117 | DESCRIPTION:(董景新;必修;1-13周;六教6A213) 118 | DTSTART;TZID=Asia/Shanghai:20130228T095000 119 | DTEND;TZID=Asia/Shanghai:20130228T121500 120 | RRULE:FREQ=WEEKLY;COUNT=16 121 | EXDATE;TZID=Asia/Shanghai:20130530T095000 122 | EXDATE;TZID=Asia/Shanghai:20130606T095000 123 | EXDATE;TZID=Asia/Shanghai:20130613T095000 124 | 125 | SEQUENCE:0 126 | STATUS:CONFIRMED 127 | END:VEVENT 128 | BEGIN:VEVENT 129 | SUMMARY:测试与检测技术基础 130 | LOCATION:五教5102 131 | DESCRIPTION:(王伯雄;必修;全周;五教5102) 132 | DTSTART;TZID=Asia/Shanghai:20130301T095000 133 | DTEND;TZID=Asia/Shanghai:20130301T121500 134 | RRULE:FREQ=WEEKLY;COUNT=16 135 | 136 | SEQUENCE:0 137 | STATUS:CONFIRMED 138 | END:VEVENT 139 | BEGIN:VEVENT 140 | SUMMARY:电磁兼容设计 141 | LOCATION:六教6A103 142 | DESCRIPTION:(倪建平;任选;全周;六教6A103) 143 | DTSTART;TZID=Asia/Shanghai:20130301T133000 144 | DTEND;TZID=Asia/Shanghai:20130301T150500 145 | RRULE:FREQ=WEEKLY;COUNT=16 146 | 147 | SEQUENCE:0 148 | STATUS:CONFIRMED 149 | END:VEVENT 150 | BEGIN:VEVENT 151 | SUMMARY:三年级男生网球 152 | LOCATION:紫荆网球场 153 | DESCRIPTION:(刘铁一;全周;紫荆网球场) 154 | DTSTART;TZID=Asia/Shanghai:20130301T152000 155 | DTEND;TZID=Asia/Shanghai:20130301T165500 156 | RRULE:FREQ=WEEKLY;COUNT=16 157 | 158 | SEQUENCE:0 159 | STATUS:CONFIRMED 160 | END:VEVENT 161 | BEGIN:VEVENT 162 | SUMMARY:制造工程基础 [F1] 163 | LOCATION:精仪系馆一层CIMS大厅 164 | DESCRIPTION:F1(精仪系馆一层CIMS大厅;第8周;时间13:00-14:30) 165 | DTSTART;TZID=Asia/Shanghai:20130225T130000 166 | DTEND;TZID=Asia/Shanghai:20130225T143000 167 | RRULE:FREQ=WEEKLY;COUNT=16 168 | EXDATE;TZID=Asia/Shanghai:20130225T130000 169 | EXDATE;TZID=Asia/Shanghai:20130304T130000 170 | EXDATE;TZID=Asia/Shanghai:20130311T130000 171 | EXDATE;TZID=Asia/Shanghai:20130318T130000 172 | EXDATE;TZID=Asia/Shanghai:20130325T130000 173 | EXDATE;TZID=Asia/Shanghai:20130401T130000 174 | EXDATE;TZID=Asia/Shanghai:20130408T130000 175 | EXDATE;TZID=Asia/Shanghai:20130422T130000 176 | EXDATE;TZID=Asia/Shanghai:20130429T130000 177 | EXDATE;TZID=Asia/Shanghai:20130506T130000 178 | EXDATE;TZID=Asia/Shanghai:20130513T130000 179 | EXDATE;TZID=Asia/Shanghai:20130520T130000 180 | EXDATE;TZID=Asia/Shanghai:20130527T130000 181 | EXDATE;TZID=Asia/Shanghai:20130603T130000 182 | EXDATE;TZID=Asia/Shanghai:20130610T130000 183 | 184 | SEQUENCE:0 185 | STATUS:CONFIRMED 186 | END:VEVENT 187 | BEGIN:VEVENT 188 | SUMMARY:制造工程基础 [S1] 189 | LOCATION:精仪系馆一层CIMS大厅 190 | DESCRIPTION:S1(精仪系馆一层CIMS大厅;第12周;时间13:00-15:00) 191 | DTSTART;TZID=Asia/Shanghai:20130225T130000 192 | DTEND;TZID=Asia/Shanghai:20130225T150000 193 | RRULE:FREQ=WEEKLY;COUNT=16 194 | EXDATE;TZID=Asia/Shanghai:20130225T130000 195 | EXDATE;TZID=Asia/Shanghai:20130304T130000 196 | EXDATE;TZID=Asia/Shanghai:20130311T130000 197 | EXDATE;TZID=Asia/Shanghai:20130318T130000 198 | EXDATE;TZID=Asia/Shanghai:20130325T130000 199 | EXDATE;TZID=Asia/Shanghai:20130401T130000 200 | EXDATE;TZID=Asia/Shanghai:20130408T130000 201 | EXDATE;TZID=Asia/Shanghai:20130415T130000 202 | EXDATE;TZID=Asia/Shanghai:20130422T130000 203 | EXDATE;TZID=Asia/Shanghai:20130429T130000 204 | EXDATE;TZID=Asia/Shanghai:20130506T130000 205 | EXDATE;TZID=Asia/Shanghai:20130520T130000 206 | EXDATE;TZID=Asia/Shanghai:20130527T130000 207 | EXDATE;TZID=Asia/Shanghai:20130603T130000 208 | EXDATE;TZID=Asia/Shanghai:20130610T130000 209 | 210 | SEQUENCE:0 211 | STATUS:CONFIRMED 212 | END:VEVENT 213 | BEGIN:VEVENT 214 | SUMMARY:控制工程基础 [04] 215 | LOCATION:精仪系馆4101 216 | DESCRIPTION:04(精仪系馆4101;第13周;时间14:00-17:00) 217 | DTSTART;TZID=Asia/Shanghai:20130226T140000 218 | DTEND;TZID=Asia/Shanghai:20130226T170000 219 | RRULE:FREQ=WEEKLY;COUNT=16 220 | EXDATE;TZID=Asia/Shanghai:20130226T140000 221 | EXDATE;TZID=Asia/Shanghai:20130305T140000 222 | EXDATE;TZID=Asia/Shanghai:20130312T140000 223 | EXDATE;TZID=Asia/Shanghai:20130319T140000 224 | EXDATE;TZID=Asia/Shanghai:20130326T140000 225 | EXDATE;TZID=Asia/Shanghai:20130402T140000 226 | EXDATE;TZID=Asia/Shanghai:20130409T140000 227 | EXDATE;TZID=Asia/Shanghai:20130416T140000 228 | EXDATE;TZID=Asia/Shanghai:20130423T140000 229 | EXDATE;TZID=Asia/Shanghai:20130430T140000 230 | EXDATE;TZID=Asia/Shanghai:20130507T140000 231 | EXDATE;TZID=Asia/Shanghai:20130514T140000 232 | EXDATE;TZID=Asia/Shanghai:20130528T140000 233 | EXDATE;TZID=Asia/Shanghai:20130604T140000 234 | EXDATE;TZID=Asia/Shanghai:20130611T140000 235 | 236 | SEQUENCE:0 237 | STATUS:CONFIRMED 238 | END:VEVENT 239 | BEGIN:VEVENT 240 | SUMMARY:实验室科研探究 (I) [(集群计算机与科学计算)05_24] 241 | LOCATION:FIT楼2-D03 242 | DESCRIPTION:(集群计算机与科学计算)05_24( FIT楼2-D03;第5周) 243 | DTSTART;TZID=Asia/Shanghai:20130226T152000 244 | DTEND;TZID=Asia/Shanghai:20130226T165500 245 | RRULE:FREQ=WEEKLY;COUNT=16 246 | EXDATE;TZID=Asia/Shanghai:20130226T152000 247 | EXDATE;TZID=Asia/Shanghai:20130305T152000 248 | EXDATE;TZID=Asia/Shanghai:20130312T152000 249 | EXDATE;TZID=Asia/Shanghai:20130319T152000 250 | EXDATE;TZID=Asia/Shanghai:20130402T152000 251 | EXDATE;TZID=Asia/Shanghai:20130409T152000 252 | EXDATE;TZID=Asia/Shanghai:20130416T152000 253 | EXDATE;TZID=Asia/Shanghai:20130423T152000 254 | EXDATE;TZID=Asia/Shanghai:20130430T152000 255 | EXDATE;TZID=Asia/Shanghai:20130507T152000 256 | EXDATE;TZID=Asia/Shanghai:20130514T152000 257 | EXDATE;TZID=Asia/Shanghai:20130521T152000 258 | EXDATE;TZID=Asia/Shanghai:20130528T152000 259 | EXDATE;TZID=Asia/Shanghai:20130604T152000 260 | EXDATE;TZID=Asia/Shanghai:20130611T152000 261 | 262 | SEQUENCE:0 263 | STATUS:CONFIRMED 264 | END:VEVENT 265 | BEGIN:VEVENT 266 | SUMMARY:实验室科研探究 (I) [(室内外光环境设计)07_24] 267 | LOCATION:美院照明与色彩实验室 268 | DESCRIPTION:(室内外光环境设计)07_24( 美院照明与色彩实验室;第7周) 269 | DTSTART;TZID=Asia/Shanghai:20130226T152000 270 | DTEND;TZID=Asia/Shanghai:20130226T165500 271 | RRULE:FREQ=WEEKLY;COUNT=16 272 | EXDATE;TZID=Asia/Shanghai:20130226T152000 273 | EXDATE;TZID=Asia/Shanghai:20130305T152000 274 | EXDATE;TZID=Asia/Shanghai:20130312T152000 275 | EXDATE;TZID=Asia/Shanghai:20130319T152000 276 | EXDATE;TZID=Asia/Shanghai:20130326T152000 277 | EXDATE;TZID=Asia/Shanghai:20130402T152000 278 | EXDATE;TZID=Asia/Shanghai:20130416T152000 279 | EXDATE;TZID=Asia/Shanghai:20130423T152000 280 | EXDATE;TZID=Asia/Shanghai:20130430T152000 281 | EXDATE;TZID=Asia/Shanghai:20130507T152000 282 | EXDATE;TZID=Asia/Shanghai:20130514T152000 283 | EXDATE;TZID=Asia/Shanghai:20130521T152000 284 | EXDATE;TZID=Asia/Shanghai:20130528T152000 285 | EXDATE;TZID=Asia/Shanghai:20130604T152000 286 | EXDATE;TZID=Asia/Shanghai:20130611T152000 287 | 288 | SEQUENCE:0 289 | STATUS:CONFIRMED 290 | END:VEVENT 291 | BEGIN:VEVENT 292 | SUMMARY:实验室科研探究 (I) [(无处不在的液压系统)04_43] 293 | LOCATION:训练中心实习车间铣磨教室 294 | DESCRIPTION:(无处不在的液压系统)04_43( 训练中心实习车间铣磨教室;第4周) 295 | DTSTART;TZID=Asia/Shanghai:20130228T133000 296 | DTEND;TZID=Asia/Shanghai:20130228T150500 297 | RRULE:FREQ=WEEKLY;COUNT=16 298 | EXDATE;TZID=Asia/Shanghai:20130228T133000 299 | EXDATE;TZID=Asia/Shanghai:20130307T133000 300 | EXDATE;TZID=Asia/Shanghai:20130314T133000 301 | EXDATE;TZID=Asia/Shanghai:20130328T133000 302 | EXDATE;TZID=Asia/Shanghai:20130404T133000 303 | EXDATE;TZID=Asia/Shanghai:20130411T133000 304 | EXDATE;TZID=Asia/Shanghai:20130418T133000 305 | EXDATE;TZID=Asia/Shanghai:20130425T133000 306 | EXDATE;TZID=Asia/Shanghai:20130502T133000 307 | EXDATE;TZID=Asia/Shanghai:20130509T133000 308 | EXDATE;TZID=Asia/Shanghai:20130516T133000 309 | EXDATE;TZID=Asia/Shanghai:20130523T133000 310 | EXDATE;TZID=Asia/Shanghai:20130530T133000 311 | EXDATE;TZID=Asia/Shanghai:20130606T133000 312 | EXDATE;TZID=Asia/Shanghai:20130613T133000 313 | 314 | SEQUENCE:0 315 | STATUS:CONFIRMED 316 | END:VEVENT 317 | BEGIN:VEVENT 318 | SUMMARY:实验室科研探究 (I) [(概论课)02_44] 319 | LOCATION:建筑馆报告厅或看网络学堂通知 320 | DESCRIPTION:(概论课)02_44( 建筑馆报告厅或看网络学堂通知;第2周) 321 | DTSTART;TZID=Asia/Shanghai:20130228T152000 322 | DTEND;TZID=Asia/Shanghai:20130228T165500 323 | RRULE:FREQ=WEEKLY;COUNT=16 324 | EXDATE;TZID=Asia/Shanghai:20130228T152000 325 | EXDATE;TZID=Asia/Shanghai:20130314T152000 326 | EXDATE;TZID=Asia/Shanghai:20130321T152000 327 | EXDATE;TZID=Asia/Shanghai:20130328T152000 328 | EXDATE;TZID=Asia/Shanghai:20130404T152000 329 | EXDATE;TZID=Asia/Shanghai:20130411T152000 330 | EXDATE;TZID=Asia/Shanghai:20130418T152000 331 | EXDATE;TZID=Asia/Shanghai:20130425T152000 332 | EXDATE;TZID=Asia/Shanghai:20130502T152000 333 | EXDATE;TZID=Asia/Shanghai:20130509T152000 334 | EXDATE;TZID=Asia/Shanghai:20130516T152000 335 | EXDATE;TZID=Asia/Shanghai:20130523T152000 336 | EXDATE;TZID=Asia/Shanghai:20130530T152000 337 | EXDATE;TZID=Asia/Shanghai:20130606T152000 338 | EXDATE;TZID=Asia/Shanghai:20130613T152000 339 | 340 | SEQUENCE:0 341 | STATUS:CONFIRMED 342 | END:VEVENT 343 | BEGIN:VEVENT 344 | SUMMARY:实验室科研探究 (I) [(人机系统仿真)08_44] 345 | LOCATION:舜德楼南529 346 | DESCRIPTION:(人机系统仿真)08_44( 舜德楼南529;第8周) 347 | DTSTART;TZID=Asia/Shanghai:20130228T152000 348 | DTEND;TZID=Asia/Shanghai:20130228T165500 349 | RRULE:FREQ=WEEKLY;COUNT=16 350 | EXDATE;TZID=Asia/Shanghai:20130228T152000 351 | EXDATE;TZID=Asia/Shanghai:20130307T152000 352 | EXDATE;TZID=Asia/Shanghai:20130314T152000 353 | EXDATE;TZID=Asia/Shanghai:20130321T152000 354 | EXDATE;TZID=Asia/Shanghai:20130328T152000 355 | EXDATE;TZID=Asia/Shanghai:20130404T152000 356 | EXDATE;TZID=Asia/Shanghai:20130411T152000 357 | EXDATE;TZID=Asia/Shanghai:20130425T152000 358 | EXDATE;TZID=Asia/Shanghai:20130502T152000 359 | EXDATE;TZID=Asia/Shanghai:20130509T152000 360 | EXDATE;TZID=Asia/Shanghai:20130516T152000 361 | EXDATE;TZID=Asia/Shanghai:20130523T152000 362 | EXDATE;TZID=Asia/Shanghai:20130530T152000 363 | EXDATE;TZID=Asia/Shanghai:20130606T152000 364 | EXDATE;TZID=Asia/Shanghai:20130613T152000 365 | 366 | SEQUENCE:0 367 | STATUS:CONFIRMED 368 | END:VEVENT 369 | BEGIN:VEVENT 370 | SUMMARY:实验室科研探究 (I) [(仿人机器人技术暨RoboCup机器人足球赛)05_44] 371 | LOCATION:精仪系馆2501 372 | DESCRIPTION:(仿人机器人技术暨RoboCup机器人足球赛)05_44( 精仪系馆2501;第5周) 373 | DTSTART;TZID=Asia/Shanghai:20130228T152000 374 | DTEND;TZID=Asia/Shanghai:20130228T165500 375 | RRULE:FREQ=WEEKLY;COUNT=16 376 | EXDATE;TZID=Asia/Shanghai:20130228T152000 377 | EXDATE;TZID=Asia/Shanghai:20130307T152000 378 | EXDATE;TZID=Asia/Shanghai:20130314T152000 379 | EXDATE;TZID=Asia/Shanghai:20130321T152000 380 | EXDATE;TZID=Asia/Shanghai:20130404T152000 381 | EXDATE;TZID=Asia/Shanghai:20130411T152000 382 | EXDATE;TZID=Asia/Shanghai:20130418T152000 383 | EXDATE;TZID=Asia/Shanghai:20130425T152000 384 | EXDATE;TZID=Asia/Shanghai:20130502T152000 385 | EXDATE;TZID=Asia/Shanghai:20130509T152000 386 | EXDATE;TZID=Asia/Shanghai:20130516T152000 387 | EXDATE;TZID=Asia/Shanghai:20130523T152000 388 | EXDATE;TZID=Asia/Shanghai:20130530T152000 389 | EXDATE;TZID=Asia/Shanghai:20130606T152000 390 | EXDATE;TZID=Asia/Shanghai:20130613T152000 391 | 392 | SEQUENCE:0 393 | STATUS:CONFIRMED 394 | END:VEVENT 395 | BEGIN:VEVENT 396 | SUMMARY:实验室科研探究 (I) [(电力电子变换与电机控制)04_44] 397 | LOCATION:西主楼1区101室 398 | DESCRIPTION:(电力电子变换与电机控制)04_44( 西主楼1区101室;第4周) 399 | DTSTART;TZID=Asia/Shanghai:20130228T152000 400 | DTEND;TZID=Asia/Shanghai:20130228T165500 401 | RRULE:FREQ=WEEKLY;COUNT=16 402 | EXDATE;TZID=Asia/Shanghai:20130228T152000 403 | EXDATE;TZID=Asia/Shanghai:20130307T152000 404 | EXDATE;TZID=Asia/Shanghai:20130314T152000 405 | EXDATE;TZID=Asia/Shanghai:20130328T152000 406 | EXDATE;TZID=Asia/Shanghai:20130404T152000 407 | EXDATE;TZID=Asia/Shanghai:20130411T152000 408 | EXDATE;TZID=Asia/Shanghai:20130418T152000 409 | EXDATE;TZID=Asia/Shanghai:20130425T152000 410 | EXDATE;TZID=Asia/Shanghai:20130502T152000 411 | EXDATE;TZID=Asia/Shanghai:20130509T152000 412 | EXDATE;TZID=Asia/Shanghai:20130516T152000 413 | EXDATE;TZID=Asia/Shanghai:20130523T152000 414 | EXDATE;TZID=Asia/Shanghai:20130530T152000 415 | EXDATE;TZID=Asia/Shanghai:20130606T152000 416 | EXDATE;TZID=Asia/Shanghai:20130613T152000 417 | 418 | SEQUENCE:0 419 | STATUS:CONFIRMED 420 | END:VEVENT 421 | BEGIN:VEVENT 422 | SUMMARY:实验室科研探究 (I) [(照猫画猫——制造业的反求技术)07_44] 423 | LOCATION:训练中心快速成型实验室 424 | DESCRIPTION:(照猫画猫——制造业的反求技术)07_44( 训练中心快速成型实验室;第7周) 425 | DTSTART;TZID=Asia/Shanghai:20130228T152000 426 | DTEND;TZID=Asia/Shanghai:20130228T165500 427 | RRULE:FREQ=WEEKLY;COUNT=16 428 | EXDATE;TZID=Asia/Shanghai:20130228T152000 429 | EXDATE;TZID=Asia/Shanghai:20130307T152000 430 | EXDATE;TZID=Asia/Shanghai:20130314T152000 431 | EXDATE;TZID=Asia/Shanghai:20130321T152000 432 | EXDATE;TZID=Asia/Shanghai:20130328T152000 433 | EXDATE;TZID=Asia/Shanghai:20130404T152000 434 | EXDATE;TZID=Asia/Shanghai:20130418T152000 435 | EXDATE;TZID=Asia/Shanghai:20130425T152000 436 | EXDATE;TZID=Asia/Shanghai:20130502T152000 437 | EXDATE;TZID=Asia/Shanghai:20130509T152000 438 | EXDATE;TZID=Asia/Shanghai:20130516T152000 439 | EXDATE;TZID=Asia/Shanghai:20130523T152000 440 | EXDATE;TZID=Asia/Shanghai:20130530T152000 441 | EXDATE;TZID=Asia/Shanghai:20130606T152000 442 | EXDATE;TZID=Asia/Shanghai:20130613T152000 443 | 444 | SEQUENCE:0 445 | STATUS:CONFIRMED 446 | END:VEVENT 447 | END:VCALENDAR 448 | 449 | -------------------------------------------------------------------------------- /exp/20130223 initial prototype/parse_G.iced: -------------------------------------------------------------------------------- 1 | window.parseRegLoc=(infoStr)-> 2 | infoStr.match(/// 3 | [\(;] # last delimiter 4 | ( #1 5 | [^\(;]+ # location (no delimeter) 6 | ) 7 | \)$ 8 | ///)?[1] 9 | 10 | window.parseLabInfo=(infoStr)-> 11 | if (match=infoStr.match(/// 12 | ( #1 13 | .* # everything before the last parens pair => labName 14 | ) 15 | \( 16 | ( #2 17 | [^\);]+ # first field in parens => loc 18 | ) 19 | .* # ignore the rest 20 | \)$ 21 | ///))? 22 | labName: match[1].trim() 23 | loc: match[2].trim() 24 | else 25 | labName: '' 26 | loc: '' 27 | 28 | window.parse_G=parse_G=(root)-> 29 | Gr=buildArray([1..7], [1..6]) 30 | Gl=buildArray([1..7], [1..6]) 31 | for z in [1..7] by 1 32 | for p in [1..6] by 1 33 | Gr[z][p]=[] 34 | Gl[z][p]=[] 35 | cell=$("#a#{p}_#{z}") 36 | 37 | # regular 38 | cell.find('a.mainHref').each(-> 39 | infoStr=@nextSibling.data.trim() 40 | loc=parseRegLoc(infoStr) 41 | {beginT, endT}=period[p] 42 | Gr[z][p].push { 43 | name : @innerText.trim() 44 | infoStr 45 | loc 46 | labName : '' 47 | week : parseWeekStr(infoStr) 48 | beginT 49 | endT 50 | } 51 | ) 52 | 53 | # lab 54 | cell.find('a.blue_red_none').each(-> 55 | infoStr=@nextElementSibling.innerText.trim() 56 | {labName, loc}=parseLabInfo(infoStr) 57 | if (t=parseTimeStr(infoStr))? 58 | {beginT, endT}=t 59 | else 60 | {beginT, endT}=period[p] 61 | Gl[z][p].push { 62 | name : @innerText.trim() 63 | infoStr 64 | loc 65 | labName 66 | week : parseWeekStr(infoStr) 67 | beginT 68 | endT 69 | } 70 | ) 71 | {Gr, Gl} 72 | -------------------------------------------------------------------------------- /exp/20130223 initial prototype/parse_G.js: -------------------------------------------------------------------------------- 1 | // Generated by IcedCoffeeScript 1.4.0b 2 | (function() { 3 | var parse_G; 4 | 5 | window.parseRegLoc = function(infoStr) { 6 | var _ref; 7 | return (_ref = infoStr.match(/[\(;]([^\(;]+)\)$/)) != null ? _ref[1] : void 0; 8 | }; 9 | 10 | window.parseLabInfo = function(infoStr) { 11 | var match; 12 | if ((match = infoStr.match(/(.*)\(([^\);]+).*\)$/)) != null) { 13 | return { 14 | labName: match[1].trim(), 15 | loc: match[2].trim() 16 | }; 17 | } else { 18 | return { 19 | labName: '', 20 | loc: '' 21 | }; 22 | } 23 | }; 24 | 25 | window.parse_G = parse_G = function(root) { 26 | var Gl, Gr, cell, p, z, _i, _j; 27 | Gr = buildArray([1, 2, 3, 4, 5, 6, 7], [1, 2, 3, 4, 5, 6]); 28 | Gl = buildArray([1, 2, 3, 4, 5, 6, 7], [1, 2, 3, 4, 5, 6]); 29 | for (z = _i = 1; _i <= 7; z = _i += 1) { 30 | for (p = _j = 1; _j <= 6; p = _j += 1) { 31 | Gr[z][p] = []; 32 | Gl[z][p] = []; 33 | cell = $("#a" + p + "_" + z); 34 | cell.find('a.mainHref').each(function() { 35 | var beginT, endT, infoStr, loc, _ref; 36 | infoStr = this.nextSibling.data.trim(); 37 | loc = parseRegLoc(infoStr); 38 | _ref = period[p], beginT = _ref.beginT, endT = _ref.endT; 39 | return Gr[z][p].push({ 40 | name: this.innerText.trim(), 41 | infoStr: infoStr, 42 | loc: loc, 43 | labName: '', 44 | week: parseWeekStr(infoStr), 45 | beginT: beginT, 46 | endT: endT 47 | }); 48 | }); 49 | cell.find('a.blue_red_none').each(function() { 50 | var beginT, endT, infoStr, labName, loc, t, _ref, _ref1; 51 | infoStr = this.nextElementSibling.innerText.trim(); 52 | _ref = parseLabInfo(infoStr), labName = _ref.labName, loc = _ref.loc; 53 | if ((t = parseTimeStr(infoStr)) != null) { 54 | beginT = t.beginT, endT = t.endT; 55 | } else { 56 | _ref1 = period[p], beginT = _ref1.beginT, endT = _ref1.endT; 57 | } 58 | return Gl[z][p].push({ 59 | name: this.innerText.trim(), 60 | infoStr: infoStr, 61 | loc: loc, 62 | labName: labName, 63 | week: parseWeekStr(infoStr), 64 | beginT: beginT, 65 | endT: endT 66 | }); 67 | }); 68 | } 69 | } 70 | return { 71 | Gr: Gr, 72 | Gl: Gl 73 | }; 74 | }; 75 | 76 | }).call(this); 77 | -------------------------------------------------------------------------------- /exp/20130223 initial prototype/parse_L.iced: -------------------------------------------------------------------------------- 1 | window.parse_L=parse_L=(root, termIdP)-> 2 | days=root.find('td.doc_title').map(-> 3 | if !(match=/(\d+)月(\d+)日/.exec @innerHTML)? then throw Error @innerHTML 4 | md=inferYear(termIdP, parseInt(match[1]), parseInt(match[2])) 5 | ) 6 | tables=root.find('> table.data_list_table') 7 | for ymd, i in days 8 | items=$(tables[i]).find('tr:not(.data_list_title)').map(-> 9 | fields=$(this).find('td').map(->@innerHTML) 10 | { 11 | beginT: getTOffset(moment(fields[0], 'HHmm')) 12 | endT : getTOffset(moment(fields[1], 'HHmm')) 13 | name : fields[3] 14 | loc : fields[4] 15 | } 16 | ) 17 | {ymd, items} 18 | -------------------------------------------------------------------------------- /exp/20130223 initial prototype/parse_L.js: -------------------------------------------------------------------------------- 1 | // Generated by IcedCoffeeScript 1.4.0b 2 | (function() { 3 | var parse_L; 4 | 5 | window.parse_L = parse_L = function(root, termIdP) { 6 | var days, i, items, tables, ymd, _i, _len, _results; 7 | days = root.find('td.doc_title').map(function() { 8 | var match, md; 9 | if ((match = /(\d+)月(\d+)日/.exec(this.innerHTML)) == null) { 10 | throw Error(this.innerHTML); 11 | } 12 | return md = inferYear(termIdP, parseInt(match[1]), parseInt(match[2])); 13 | }); 14 | tables = root.find('> table.data_list_table'); 15 | _results = []; 16 | for (i = _i = 0, _len = days.length; _i < _len; i = ++_i) { 17 | ymd = days[i]; 18 | items = $(tables[i]).find('tr:not(.data_list_title)').map(function() { 19 | var fields; 20 | fields = $(this).find('td').map(function() { 21 | return this.innerHTML; 22 | }); 23 | return { 24 | beginT: getTOffset(moment(fields[0], 'HHmm')), 25 | endT: getTOffset(moment(fields[1], 'HHmm')), 26 | name: fields[3], 27 | loc: fields[4] 28 | }; 29 | }); 30 | _results.push({ 31 | ymd: ymd, 32 | items: items 33 | }); 34 | } 35 | return _results; 36 | }; 37 | 38 | }).call(this); 39 | -------------------------------------------------------------------------------- /exp/20130223 initial prototype/thucal2.user.iced: -------------------------------------------------------------------------------- 1 | ` 2 | // vim: nowrap 3 | // Copyright (c) 2013, smilekzs. (MIT Licensed) 4 | // ==UserScript== 5 | // @name thucal2 6 | // @namespace http://github.com/smilekzs 7 | // @version 0.2.1 8 | // @description Export Tsinghua University undergraduate curriculum to iCalendar 9 | // @include *.cic.tsinghua.edu.cn/syxk.vsyxkKcapb.do* 10 | // ==/UserScript== 11 | 12 | //#include 13 | ` 14 | 15 | 16 | ############ 17 | ## common 18 | window.buildArray=buildArray=(dims...)-> 19 | if !(dims?.length) then return null 20 | d=dims?.shift() 21 | if d.length? 22 | a=[] 23 | for i in d 24 | a[i]=buildArray(dims...) 25 | else 26 | a=new Array(d) 27 | for i in [0...d] by 1 28 | a[i]=buildArray(dims...) 29 | a 30 | 31 | window.inferYear=inferYear=(termIdP, m, d)-> 32 | md=moment().month(m-1).date(d) 33 | th=moment().month(5-1).date(1) 34 | if termIdP.termN==1 && md.isAfter th 35 | y=termIdP.beginY 36 | else 37 | y=termIdP.endY 38 | moment([y, m-1, d]) 39 | 40 | window.getTOffset=getTOffset=(t)->t.clone().diff(t.clone().startOf('day')) 41 | 42 | window.period=period=[ 43 | "00:00" # placeholder 44 | "08:00" #1 45 | "09:50" #2 46 | "13:30" #3 47 | "15:20" #4 48 | "17:05" #5 49 | "19:20" #6 50 | ].map((s)-> 51 | t=moment(s, 'HHmm') 52 | { 53 | beginT: getTOffset(t) 54 | endT : getTOffset(t.add(1, 'hours').add(35, 'minutes')) 55 | } 56 | ) 57 | 58 | window.parseTermId=parseTermId=(termId)-> 59 | if !(match=/(\d{4})-(\d{4})-(\d)/.exec termId)? then return null 60 | { 61 | beginY: parseInt match[1] 62 | endY : parseInt match[2] 63 | termN : parseInt match[3] 64 | } 65 | 66 | window.printTermId=(termIdP)-> 67 | termIdP.beginY+'-'+termIdP.endY+'-'+( 68 | switch termIdP.termN 69 | when 1 then '秋' 70 | when 2 then '春' 71 | else '不科学' 72 | ) 73 | 74 | window.parseTimeStr=parseTimeStr=(infoStr)-> 75 | if !(match=/时间(\d{1,2}:\d{1,2})-(\d{1,2}:\d{1,2})/.exec(infoStr))? then return null 76 | { 77 | beginT: getTOffset(moment(match[1], 'HHmm')) 78 | endT : getTOffset(moment(match[2], 'HHmm')) 79 | } 80 | 81 | window.parseWeekStr=parseWeekStr=(weekStr)-> 82 | if !(part=/(([\d,-]+)|全|前八|后八|单|双)周/.exec(weekStr)) then return null 83 | switch part[1].charAt(0) 84 | when '全' then return [1..16] 85 | when '前' then return [1..8] 86 | when '后' then return [9..16] 87 | when '单' then return (w for w in [1..16] by 2) 88 | when '双' then return (w for w in [2..16] by 2) 89 | else 90 | ret=[] 91 | for s in part[2].split(',') 92 | #isolated? 93 | if (match=/^\d+$/.exec(s))? 94 | ret.push(Number(match[0])) 95 | #range? 96 | if (match=/^(\d+)-(\d+)$/.exec(s))? 97 | ret.push(w) for w in [Number(match[1])..Number(match[2])] by 1 98 | ret 99 | 100 | 101 | ############ 102 | ## parse_L 103 | window.parse_L=parse_L=(root, termIdP)-> 104 | days=root.find('td.doc_title').map(-> 105 | if !(match=/(\d+)月(\d+)日/.exec @innerHTML)? then throw Error @innerHTML 106 | md=inferYear(termIdP, parseInt(match[1]), parseInt(match[2])) 107 | ) 108 | tables=root.find('> table.data_list_table') 109 | for ymd, i in days 110 | items=$(tables[i]).find('tr:not(.data_list_title)').map(-> 111 | fields=$(this).find('td').map(->@innerHTML) 112 | { 113 | beginT: getTOffset(moment(fields[0], 'HHmm')) 114 | endT : getTOffset(moment(fields[1], 'HHmm')) 115 | name : fields[3] 116 | loc : fields[4] 117 | } 118 | ) 119 | {ymd, items} 120 | 121 | 122 | ############ 123 | ## parse_G 124 | window.parseRegLoc=(infoStr)-> 125 | infoStr.match(/// 126 | [\(;] # last delimiter 127 | ( #1 128 | [^\(;]+ # location (no delimeter) 129 | ) 130 | \)$ 131 | ///)?[1] 132 | 133 | window.parseLabInfo=(infoStr)-> 134 | if (match=infoStr.match(/// 135 | ( #1 136 | .* # everything before the last parens pair => labName 137 | ) 138 | \( 139 | ( #2 140 | [^\);]+ # first field in parens => loc 141 | ) 142 | .* # ignore the rest 143 | \)$ 144 | ///))? 145 | labName: match[1].trim() 146 | loc: match[2].trim() 147 | else 148 | labName: '' 149 | loc: '' 150 | 151 | window.parse_G=parse_G=(root)-> 152 | Gr=buildArray([1..7], [1..6]) 153 | Gl=buildArray([1..7], [1..6]) 154 | for z in [1..7] by 1 155 | for p in [1..6] by 1 156 | Gr[z][p]=[] 157 | Gl[z][p]=[] 158 | cell=$("#a#{p}_#{z}") 159 | 160 | # regular 161 | cell.find('a.mainHref').each(-> 162 | infoStr=@nextSibling.data.trim() 163 | loc=parseRegLoc(infoStr) 164 | {beginT, endT}=period[p] 165 | Gr[z][p].push { 166 | name : @textContent.trim() 167 | infoStr 168 | loc 169 | labName : '' 170 | week : parseWeekStr(infoStr) 171 | beginT 172 | endT 173 | } 174 | ) 175 | 176 | # lab 177 | cell.find('a.blue_red_none').each(-> 178 | infoStr=@nextElementSibling.textContent.trim() 179 | {labName, loc}=parseLabInfo(infoStr) 180 | if (t=parseTimeStr(infoStr))? 181 | {beginT, endT}=t 182 | else 183 | {beginT, endT}=period[p] 184 | Gl[z][p].push { 185 | name : @textContent.trim() 186 | infoStr 187 | loc 188 | labName 189 | week : parseWeekStr(infoStr) 190 | beginT 191 | endT 192 | } 193 | ) 194 | {Gr, Gl} 195 | 196 | 197 | ############ 198 | ## combine 199 | window.getOrigin=getOrigin=(Gr, L)-> 200 | lastDay=L[L.length-1] 201 | lastItems=lastDay.items 202 | lastItem=lastItems[lastItems.length-1] 203 | z=lastDay.ymd.day() 204 | 205 | maxW=0 206 | for p in [6..1] by -1 207 | for it in Gr[z][p] 208 | if it.name==lastItem.name && (w=it.week[it.week.length-1])>maxW 209 | maxW=w 210 | 211 | lastDay.ymd.clone().subtract(maxW-1, 'weeks').subtract(z-1, 'days') 212 | 213 | window.combine=combine=(Gr, L, origin)-> 214 | # re-map L to Lrel[day-since-origin] 215 | Lrel=[] 216 | for x in L 217 | Lrel[x.ymd.diff(origin, 'days')]=x.items 218 | 219 | # override G-side attributes with L-side equivalent (if available) 220 | for z in [1..7] by 1 221 | for p in [1..6] by 1 222 | for gi in Gr[z][p] 223 | w=gi.week 224 | w=w[w.length-1] 225 | rel=(w-1)*7+(z-1) 226 | if (bin=Lrel[rel]) then for li in bin 227 | if !li.matched && li.name==gi.name 228 | li.matched=true 229 | gi.beginT=li.beginT 230 | gi.endT=li.endT 231 | gi.loc=li.loc 232 | break 233 | Gr 234 | 235 | 236 | ############ 237 | ## ical 238 | ICAL_HEADER=""" 239 | BEGIN:VCALENDAR 240 | PRODID:-//smilekzs//thucal//EN 241 | VERSION:2.0 242 | CALSCALE:GREGORIAN 243 | METHOD:PUBLISH 244 | X-WR-CALNAME:THU:2012-2013-2 245 | X-WR-TIMEZONE:Asia/Shanghai 246 | BEGIN:VTIMEZONE 247 | TZID:Asia/Shanghai 248 | X-LIC-LOCATION:Asia/Shanghai 249 | BEGIN:STANDARD 250 | TZOFFSETFROM:+0800 251 | TZOFFSETTO:+0800 252 | TZNAME:CST 253 | DTSTART:19700101T000000 254 | END:STANDARD 255 | END:VTIMEZONE 256 | 257 | """ 258 | ICAL_FOOTER=""" 259 | END:VCALENDAR 260 | 261 | """ 262 | ICAL_EVENT=""" 263 | BEGIN:VEVENT 264 | SUMMARY: 265 | LOCATION: 266 | DESCRIPTION: 267 | DTSTART;TZID=Asia/Shanghai: 268 | DTEND;TZID=Asia/Shanghai: 269 | RRULE:FREQ=WEEKLY;COUNT=16 270 | 271 | SEQUENCE:0 272 | STATUS:CONFIRMED 273 | END:VEVENT 274 | 275 | """ 276 | ICAL_EX=""" 277 | EXDATE;TZID=Asia/Shanghai: 278 | 279 | """ 280 | window.ical=ical=new -> 281 | @escape=(s)-> 282 | s.replace(/,/g, '\\,') 283 | @template=(tmpl, obj)-> 284 | ret=tmpl 285 | for k, v of obj 286 | ret=ret.replace(RegExp('<'+k+'>'), v) 287 | ret 288 | @dateStr=(base, offset)-> 289 | base.clone().add(offset).format('YYYYMMDD[T]HHmmss') 290 | @nameStr=(gi)-> 291 | ret=gi.name 292 | if gi.labName 293 | ret+=' ['+gi.labName+']' 294 | ret 295 | @makeEx=(d1, gi)-> 296 | exclude=new Array(16+1) 297 | for i in [1..16] by 1 298 | exclude[i]=true 299 | for w in gi.week 300 | exclude[w]=false 301 | ret=[] 302 | for i in [1..16] by 1 303 | if exclude[i] 304 | ret.push @template ICAL_EX, 305 | date: @dateStr(d1.clone().add(i-1, 'weeks'), gi.beginT) 306 | ret.join('') 307 | @makeG=(G, origin)-> 308 | ret=[] 309 | for z in [1..7] by 1 310 | d1=origin.clone().add(z-1, 'days') 311 | for p in [1..6] by 1 312 | for gi in G[z][p] 313 | ret.push @template ICAL_EVENT, 314 | name : @escape @nameStr gi 315 | loc : @escape gi.loc 316 | desc : @escape gi.infoStr 317 | start : @dateStr(d1, gi.beginT) 318 | end : @dateStr(d1, gi.endT ) 319 | ex : @makeEx(d1, gi) 320 | ret.join('') 321 | @make=(Gr, Gl, origin)-> 322 | return ICAL_HEADER+@makeG(Gr, origin)+@makeG(Gl, origin)+ICAL_FOOTER 323 | this 324 | 325 | 326 | ############ 327 | ## I/O 328 | window.L_URL=L_URL='http://zhjw.cic.tsinghua.edu.cn/jxmh.do' 329 | ERR_MSG_LIST='list错误:检查是否已登录http://info.tsinghua.edu.cn/' 330 | window.stringify=stringify=(p)-> 331 | (for k, v of p 332 | (encodeURIComponent(k) + '=' + encodeURIComponent(v)) 333 | ).join('&') 334 | window.get_L=get_L=(autocb)-> 335 | await GM_xmlhttpRequest { 336 | url: L_URL + '?m=bks_jxrl_all' 337 | method: 'GET' 338 | onload: defer(resp) 339 | onerror: (err)-> 340 | thucal.ui.log ERR_MSG_LIST 341 | throw Error('get_L: no token: ' + err.toString()) 342 | } 343 | if !(match=/name="token" value="([\da-f]+)"/.exec(resp.responseText))? 344 | thucal.ui.log ERR_MSG_LIST 345 | throw Error 'get_L: no token: response does not contain token' 346 | params= 347 | 'm': 'bks_jxrl_all' 348 | 'role': 'bks' 349 | 'grrlID': '' 350 | 'displayType': '' 351 | 'token': match[1] 352 | 'p_start_date': moment().format('YYYYMMDD') 353 | 'p_end_date': moment().add(1, 'years').format('YYYYMMDD') 354 | await GM_xmlhttpRequest { 355 | url: L_URL 356 | method: 'POST' 357 | headers: 358 | "Content-Type": "application/x-www-form-urlencoded" 359 | data: stringify(params) 360 | onload: defer(resp) 361 | onerror: (err)-> 362 | thucal.ui.log ERR_MSG_LIST 363 | throw Error('get_L: post error: ' + err.toString()) 364 | } 365 | $(resp.responseText).filter('a') 366 | window.download=download=(cont, name)-> 367 | b=new Blob([cont], {type: 'text/calendar'}) 368 | saveAs(b, name) 369 | 370 | 371 | ############ 372 | ## userscript logic 373 | window.thucal=thucal=new -> 374 | @init=-> 375 | @ui={} 376 | # button 377 | $('input[name="export2"]').before(""" 378 | 379 | """) 380 | @ui.button=$('#thucal_button') 381 | @ui.button.on 'click', =>@make() 382 | $('#a1_1').parentsUntil('form').last().after(""" 383 |
388 | """) 389 | @ui.status=$('#thucal_status') 390 | @ui.log=(s)->@status.append(s+'\n') 391 | 392 | @make=-> 393 | @ui.log "******THUCAL********" 394 | termIdP=parseTermId($('input[name=p_xnxq]').val()) 395 | term=printTermId(termIdP) 396 | @ui.log '学期:'+term 397 | if termIdP.termN!=1 && termIdP.termN!=2 398 | @ui.log '不支持小学期!' 399 | return 400 | 401 | await get_L defer(Lraw) 402 | @ui.log 'list完成' 403 | 404 | try 405 | L=parse_L(Lraw, termIdP) 406 | {Gr, Gl}=parse_G($(document)) 407 | origin=getOrigin(Gr, L) 408 | combine(Gr, L, origin) 409 | @ui.log '分析完成' 410 | catch e 411 | @ui.log '分析错误:'+e.toString() 412 | return console.error e 413 | 414 | ret=ical.make(Gr, Gl, origin) 415 | #console.log ret 416 | download(ret, "thucal-#{term}.ics") 417 | @ui.log '导出成功!' 418 | this 419 | 420 | $(document).ready(->thucal.init()) 421 | -------------------------------------------------------------------------------- /exp/20130223 initial prototype/week.coffee: -------------------------------------------------------------------------------- 1 | parse=(weekStr)-> 2 | r=/(([\d,-]+)|全|前八|后八|单|双)周/.exec(weekStr) 3 | return null if !r? 4 | 5 | switch r[1].charAt(0) 6 | when '全' then return [1..16] 7 | when '前' then return [1..8] 8 | when '后' then return [9..16] 9 | when '单' then return (w for w in [1..16] by 2) 10 | when '双' then return (w for w in [2..16] by 2) 11 | else 12 | ret=[] 13 | for s in r[2].split(',') 14 | #isolated? 15 | match=/^\d+$/.exec(s) 16 | if match? 17 | ret.push(Number(match[0])) 18 | #range? 19 | match=/^(\d+)-(\d+)$/.exec(s) 20 | if match? 21 | ret.push(w) for w in [Number(match[1])..Number(match[2])] 22 | ret 23 | 24 | getMask=(week)-> 25 | return null if !week? 26 | ret=0 27 | ret|=1<>1 #[1..16] -> [0..15] 29 | 30 | module.exports={ 31 | parse: parse 32 | getMask: getMask 33 | } 34 | -------------------------------------------------------------------------------- /exp/20130225 feat - week display/basic.ics: -------------------------------------------------------------------------------- 1 | BEGIN:VCALENDAR 2 | PRODID:-//Google Inc//Google Calendar 70.9054//EN 3 | VERSION:2.0 4 | CALSCALE:GREGORIAN 5 | METHOD:PUBLISH 6 | X-WR-CALNAME:x 7 | X-WR-TIMEZONE:Asia/Shanghai 8 | X-WR-CALDESC: 9 | BEGIN:VTIMEZONE 10 | TZID:Asia/Shanghai 11 | X-LIC-LOCATION:Asia/Shanghai 12 | BEGIN:STANDARD 13 | TZOFFSETFROM:+0800 14 | TZOFFSETTO:+0800 15 | TZNAME:CST 16 | DTSTART:19700101T000000 17 | END:STANDARD 18 | END:VTIMEZONE 19 | BEGIN:VEVENT 20 | DTSTART;VALUE=DATE:20130311 21 | DTEND;VALUE=DATE:20130312 22 | DTSTAMP:20130225T135405Z 23 | UID:taje0nlgq5k4oe4g8vp0ditbes@google.com 24 | RECURRENCE-ID;VALUE=DATE:20130311 25 | CREATED:20130225T135113Z 26 | DESCRIPTION: 27 | LAST-MODIFIED:20130225T135257Z 28 | LOCATION: 29 | SEQUENCE:2 30 | STATUS:CONFIRMED 31 | SUMMARY:W3 32 | TRANSP:TRANSPARENT 33 | END:VEVENT 34 | BEGIN:VEVENT 35 | DTSTART;VALUE=DATE:20130304 36 | DTEND;VALUE=DATE:20130305 37 | DTSTAMP:20130225T135405Z 38 | UID:taje0nlgq5k4oe4g8vp0ditbes@google.com 39 | RECURRENCE-ID;VALUE=DATE:20130304 40 | CREATED:20130225T135113Z 41 | DESCRIPTION: 42 | LAST-MODIFIED:20130225T135246Z 43 | LOCATION: 44 | SEQUENCE:2 45 | STATUS:CONFIRMED 46 | SUMMARY:W2 47 | TRANSP:TRANSPARENT 48 | END:VEVENT 49 | BEGIN:VEVENT 50 | DTSTART;VALUE=DATE:20130225 51 | DTEND;VALUE=DATE:20130226 52 | RRULE:FREQ=WEEKLY;COUNT=16;BYDAY=MO 53 | DTSTAMP:20130225T135405Z 54 | UID:taje0nlgq5k4oe4g8vp0ditbes@google.com 55 | CREATED:20130225T135113Z 56 | DESCRIPTION: 57 | LAST-MODIFIED:20130225T135202Z 58 | LOCATION: 59 | SEQUENCE:2 60 | STATUS:CONFIRMED 61 | SUMMARY:W1 62 | TRANSP:TRANSPARENT 63 | END:VEVENT 64 | END:VCALENDAR 65 | -------------------------------------------------------------------------------- /exp/20130225 feat - week display/test.manual.ics: -------------------------------------------------------------------------------- 1 | BEGIN:VCALENDAR 2 | PRODID:-//thucal//thucal//EN 3 | VERSION:2.0 4 | CALSCALE:GREGORIAN 5 | METHOD:PUBLISH 6 | BEGIN:VTIMEZONE 7 | TZID:Asia/Shanghai 8 | X-LIC-LOCATION:Asia/Shanghai 9 | BEGIN:STANDARD 10 | TZOFFSETFROM:+0800 11 | TZOFFSETTO:+0800 12 | TZNAME:CST 13 | DTSTART:19700101T000000 14 | END:STANDARD 15 | END:VTIMEZONE 16 | 17 | BEGIN:VEVENT 18 | DTSTART;VALUE=DATE:20130311 19 | DTEND;VALUE=DATE:20130312 20 | DTSTAMP:20130225T135405Z 21 | UID:thucal@thucal 22 | RECURRENCE-ID;VALUE=DATE:20130311 23 | SEQUENCE:2 24 | STATUS:CONFIRMED 25 | SUMMARY:W3 26 | END:VEVENT 27 | 28 | BEGIN:VEVENT 29 | DTSTART;VALUE=DATE:20130304 30 | DTEND;VALUE=DATE:20130305 31 | DTSTAMP:20130225T135405Z 32 | UID:thucal@thucal 33 | RECURRENCE-ID;VALUE=DATE:20130304 34 | SEQUENCE:2 35 | STATUS:CONFIRMED 36 | SUMMARY:W2 37 | END:VEVENT 38 | 39 | BEGIN:VEVENT 40 | DTSTART;VALUE=DATE:20130225 41 | DTEND;VALUE=DATE:20130226 42 | RRULE:FREQ=WEEKLY;COUNT=5;BYDAY=MO 43 | DTSTAMP:20130225T135405Z 44 | UID:thucal@thucal 45 | SEQUENCE:2 46 | STATUS:CONFIRMED 47 | SUMMARY:W1 48 | END:VEVENT 49 | 50 | END:VCALENDAR 51 | -------------------------------------------------------------------------------- /exp/20130225 feat - week display/test.manual2.ics: -------------------------------------------------------------------------------- 1 | BEGIN:VCALENDAR 2 | PRODID:-//thucal//thucal//EN 3 | VERSION:2.0 4 | CALSCALE:GREGORIAN 5 | METHOD:PUBLISH 6 | BEGIN:VTIMEZONE 7 | TZID:Asia/Shanghai 8 | X-LIC-LOCATION:Asia/Shanghai 9 | BEGIN:STANDARD 10 | TZOFFSETFROM:+0800 11 | TZOFFSETTO:+0800 12 | TZNAME:CST 13 | DTSTART:19700101T000000 14 | END:STANDARD 15 | END:VTIMEZONE 16 | 17 | BEGIN:VEVENT 18 | DTSTART;VALUE=DATE:20130225 19 | DTEND;VALUE=DATE:20130226 20 | RRULE:FREQ=WEEKLY;COUNT=4;BYDAY=MO 21 | DTSTAMP:20130225T135405Z 22 | UID:taje0nlgq5k4oe4g8vp0ditbes@google.com 23 | CREATED:20130225T135113Z 24 | DESCRIPTION: 25 | LAST-MODIFIED:20130225T135202Z 26 | LOCATION: 27 | SEQUENCE:2 28 | STATUS:CONFIRMED 29 | SUMMARY:W1 30 | TRANSP:TRANSPARENT 31 | END:VEVENT 32 | 33 | BEGIN:VEVENT 34 | DTSTART;VALUE=DATE:20130304 35 | DTEND;VALUE=DATE:20130305 36 | DTSTAMP:20130225T135405Z 37 | UID:taje0nlgq5k4oe4g8vp0ditbes@google.com 38 | RECURRENCE-ID;VALUE=DATE:20130304 39 | CREATED:20130225T135113Z 40 | DESCRIPTION: 41 | LAST-MODIFIED:20130225T135246Z 42 | LOCATION: 43 | SEQUENCE:2 44 | STATUS:CONFIRMED 45 | SUMMARY:W2 46 | TRANSP:TRANSPARENT 47 | END:VEVENT 48 | 49 | BEGIN:VEVENT 50 | DTSTART;VALUE=DATE:20130311 51 | DTEND;VALUE=DATE:20130312 52 | DTSTAMP:20130225T135405Z 53 | UID:taje0nlgq5k4oe4g8vp0ditbes@google.com 54 | RECURRENCE-ID;VALUE=DATE:20130311 55 | CREATED:20130225T135113Z 56 | DESCRIPTION: 57 | LAST-MODIFIED:20130225T135257Z 58 | LOCATION: 59 | SEQUENCE:2 60 | STATUS:CONFIRMED 61 | SUMMARY:W3 62 | TRANSP:TRANSPARENT 63 | END:VEVENT 64 | 65 | END:VCALENDAR 66 | -------------------------------------------------------------------------------- /exp/20130225 feat - week display/week only.ics: -------------------------------------------------------------------------------- 1 | BEGIN:VCALENDAR 2 | PRODID:-//Google Inc//Google Calendar 70.9054//EN 3 | VERSION:2.0 4 | CALSCALE:GREGORIAN 5 | METHOD:PUBLISH 6 | X-WR-CALNAME:x 7 | X-WR-TIMEZONE:Asia/Shanghai 8 | X-WR-CALDESC: 9 | BEGIN:VTIMEZONE 10 | TZID:Asia/Shanghai 11 | X-LIC-LOCATION:Asia/Shanghai 12 | BEGIN:STANDARD 13 | TZOFFSETFROM:+0800 14 | TZOFFSETTO:+0800 15 | TZNAME:CST 16 | DTSTART:19700101T000000 17 | END:STANDARD 18 | END:VTIMEZONE 19 | 20 | { 21 | SUMMARY:W1 22 | DTSTART;VALUE=DATE:20130225 23 | DTEND;VALUE=DATE:20130226 24 | RRULE:FREQ=WEEKLY;COUNT=16;BYDAY=MO 25 | SEQUENCE:2 26 | STATUS:CONFIRMED 27 | } 28 | { 29 | DTSTART;VALUE=DATE:20130304 30 | DTEND;VALUE=DATE:20130305 31 | RECURRENCE-ID;VALUE=DATE:20130304 32 | SEQUENCE:2 33 | STATUS:CONFIRMED 34 | SUMMARY:W2 35 | } 36 | { 37 | DTSTART;VALUE=DATE:20130311 38 | DTEND;VALUE=DATE:20130312 39 | RECURRENCE-ID;VALUE=DATE:20130311 40 | SEQUENCE:2 41 | STATUS:CONFIRMED 42 | SUMMARY:W3 43 | } 44 | END:VCALENDAR 45 | -------------------------------------------------------------------------------- /exp/20130225 grad test/thucal2-grad-sample.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/summivox/thucal2/a264c28bfcf8fe33bf3d6c90a25f88bcf32dd2c8/exp/20130225 grad test/thucal2-grad-sample.tar.gz -------------------------------------------------------------------------------- /lib/FileSaver.min.js: -------------------------------------------------------------------------------- 1 | /*! @source http://purl.eligrey.com/github/FileSaver.js/blob/master/FileSaver.js */ 2 | var saveAs=saveAs||navigator.msSaveBlob&&navigator.msSaveBlob.bind(navigator)||function(a){"use strict";var b=a.document,c=function(){return a.URL||a.webkitURL||a},d=a.URL||a.webkitURL||a,e=b.createElementNS("http://www.w3.org/1999/xhtml","a"),f="download"in e,g=function(c){var d=b.createEvent("MouseEvents");return d.initMouseEvent("click",!0,!1,a,0,0,0,0,0,!1,!1,!1,!1,0,null),c.dispatchEvent(d)},h=a.webkitRequestFileSystem,i=a.requestFileSystem||h||a.mozRequestFileSystem,j=function(b){(a.setImmediate||a.setTimeout)(function(){throw b},0)},k="application/octet-stream",l=0,m=[],n=function(){for(var a=m.length;a--;){var b=m[a];"string"==typeof b?d.revokeObjectURL(b):b.remove()}m.length=0},o=function(a,b,c){b=[].concat(b);for(var d=b.length;d--;){var e=a["on"+b[d]];if("function"==typeof e)try{e.call(a,c||a)}catch(f){j(f)}}},p=function(b,d){var q,r,x,j=this,n=b.type,p=!1,s=function(){var a=c().createObjectURL(b);return m.push(a),a},t=function(){o(j,"writestart progress write writeend".split(" "))},u=function(){(p||!q)&&(q=s(b)),r&&(r.location.href=q),j.readyState=j.DONE,t()},v=function(a){return function(){return j.readyState!==j.DONE?a.apply(this,arguments):void 0}},w={create:!0,exclusive:!1};return j.readyState=j.INIT,d||(d="download"),f&&(q=s(b),e.href=q,e.download=d,g(e))?(j.readyState=j.DONE,t(),void 0):(a.chrome&&n&&n!==k&&(x=b.slice||b.webkitSlice,b=x.call(b,0,b.size,k),p=!0),h&&"download"!==d&&(d+=".download"),r=n===k||h?a:a.open(),i?(l+=b.size,i(a.TEMPORARY,l,v(function(a){a.root.getDirectory("saved",w,v(function(a){var c=function(){a.getFile(d,w,v(function(a){a.createWriter(v(function(c){c.onwriteend=function(b){r.location.href=a.toURL(),m.push(a),j.readyState=j.DONE,o(j,"writeend",b)},c.onerror=function(){var a=c.error;a.code!==a.ABORT_ERR&&u()},"writestart progress write abort".split(" ").forEach(function(a){c["on"+a]=j["on"+a]}),c.write(b),j.abort=function(){c.abort(),j.readyState=j.DONE},j.readyState=j.WRITING}),u)}),u)};a.getFile(d,{create:!1},v(function(a){a.remove(),c()}),v(function(a){a.code===a.NOT_FOUND_ERR?c():u()}))}),u)}),u),void 0):(u(),void 0))},q=p.prototype,r=function(a,b){return new p(a,b)};return q.abort=function(){var a=this;a.readyState=a.DONE,o(a,"abort")},q.readyState=q.INIT=0,q.WRITING=1,q.DONE=2,q.error=q.onwritestart=q.onprogress=q.onwrite=q.onabort=q.onerror=q.onwriteend=null,a.addEventListener("unload",n,!1),r}(self); 3 | -------------------------------------------------------------------------------- /lib/moment.js: -------------------------------------------------------------------------------- 1 | // moment.js 2 | // version : 2.0.0 3 | // author : Tim Wood 4 | // license : MIT 5 | // momentjs.com 6 | 7 | (function (undefined) { 8 | 9 | /************************************ 10 | Constants 11 | ************************************/ 12 | 13 | var moment, 14 | VERSION = "2.0.0", 15 | round = Math.round, i, 16 | // internal storage for language config files 17 | languages = {}, 18 | 19 | // check for nodeJS 20 | hasModule = (typeof module !== 'undefined' && module.exports), 21 | 22 | // ASP.NET json date format regex 23 | aspNetJsonRegex = /^\/?Date\((\-?\d+)/i, 24 | 25 | // format tokens 26 | formattingTokens = /(\[[^\[]*\])|(\\)?(Mo|MM?M?M?|Do|DDDo|DD?D?D?|ddd?d?|do?|w[o|w]?|W[o|W]?|YYYYY|YYYY|YY|a|A|hh?|HH?|mm?|ss?|SS?S?|X|zz?|ZZ?|.)/g, 27 | localFormattingTokens = /(\[[^\[]*\])|(\\)?(LT|LL?L?L?|l{1,4})/g, 28 | 29 | // parsing tokens 30 | parseMultipleFormatChunker = /([0-9a-zA-Z\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+)/gi, 31 | 32 | // parsing token regexes 33 | parseTokenOneOrTwoDigits = /\d\d?/, // 0 - 99 34 | parseTokenOneToThreeDigits = /\d{1,3}/, // 0 - 999 35 | parseTokenThreeDigits = /\d{3}/, // 000 - 999 36 | parseTokenFourDigits = /\d{1,4}/, // 0 - 9999 37 | parseTokenSixDigits = /[+\-]?\d{1,6}/, // -999,999 - 999,999 38 | parseTokenWord = /[0-9]*[a-z\u00A0-\u05FF\u0700-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+|[\u0600-\u06FF]+\s*?[\u0600-\u06FF]+/i, // any word (or two) characters or numbers including two word month in arabic. 39 | parseTokenTimezone = /Z|[\+\-]\d\d:?\d\d/i, // +00:00 -00:00 +0000 -0000 or Z 40 | parseTokenT = /T/i, // T (ISO seperator) 41 | parseTokenTimestampMs = /[\+\-]?\d+(\.\d{1,3})?/, // 123456789 123456789.123 42 | 43 | // preliminary iso regex 44 | // 0000-00-00 + T + 00 or 00:00 or 00:00:00 or 00:00:00.000 + +00:00 or +0000 45 | isoRegex = /^\s*\d{4}-\d\d-\d\d((T| )(\d\d(:\d\d(:\d\d(\.\d\d?\d?)?)?)?)?([\+\-]\d\d:?\d\d)?)?/, 46 | isoFormat = 'YYYY-MM-DDTHH:mm:ssZ', 47 | 48 | // iso time formats and regexes 49 | isoTimes = [ 50 | ['HH:mm:ss.S', /(T| )\d\d:\d\d:\d\d\.\d{1,3}/], 51 | ['HH:mm:ss', /(T| )\d\d:\d\d:\d\d/], 52 | ['HH:mm', /(T| )\d\d:\d\d/], 53 | ['HH', /(T| )\d\d/] 54 | ], 55 | 56 | // timezone chunker "+10:00" > ["10", "00"] or "-1530" > ["-15", "30"] 57 | parseTimezoneChunker = /([\+\-]|\d\d)/gi, 58 | 59 | // getter and setter names 60 | proxyGettersAndSetters = 'Month|Date|Hours|Minutes|Seconds|Milliseconds'.split('|'), 61 | unitMillisecondFactors = { 62 | 'Milliseconds' : 1, 63 | 'Seconds' : 1e3, 64 | 'Minutes' : 6e4, 65 | 'Hours' : 36e5, 66 | 'Days' : 864e5, 67 | 'Months' : 2592e6, 68 | 'Years' : 31536e6 69 | }, 70 | 71 | // format function strings 72 | formatFunctions = {}, 73 | 74 | // tokens to ordinalize and pad 75 | ordinalizeTokens = 'DDD w W M D d'.split(' '), 76 | paddedTokens = 'M D H h m s w W'.split(' '), 77 | 78 | formatTokenFunctions = { 79 | M : function () { 80 | return this.month() + 1; 81 | }, 82 | MMM : function (format) { 83 | return this.lang().monthsShort(this, format); 84 | }, 85 | MMMM : function (format) { 86 | return this.lang().months(this, format); 87 | }, 88 | D : function () { 89 | return this.date(); 90 | }, 91 | DDD : function () { 92 | return this.dayOfYear(); 93 | }, 94 | d : function () { 95 | return this.day(); 96 | }, 97 | dd : function (format) { 98 | return this.lang().weekdaysMin(this, format); 99 | }, 100 | ddd : function (format) { 101 | return this.lang().weekdaysShort(this, format); 102 | }, 103 | dddd : function (format) { 104 | return this.lang().weekdays(this, format); 105 | }, 106 | w : function () { 107 | return this.week(); 108 | }, 109 | W : function () { 110 | return this.isoWeek(); 111 | }, 112 | YY : function () { 113 | return leftZeroFill(this.year() % 100, 2); 114 | }, 115 | YYYY : function () { 116 | return leftZeroFill(this.year(), 4); 117 | }, 118 | YYYYY : function () { 119 | return leftZeroFill(this.year(), 5); 120 | }, 121 | a : function () { 122 | return this.lang().meridiem(this.hours(), this.minutes(), true); 123 | }, 124 | A : function () { 125 | return this.lang().meridiem(this.hours(), this.minutes(), false); 126 | }, 127 | H : function () { 128 | return this.hours(); 129 | }, 130 | h : function () { 131 | return this.hours() % 12 || 12; 132 | }, 133 | m : function () { 134 | return this.minutes(); 135 | }, 136 | s : function () { 137 | return this.seconds(); 138 | }, 139 | S : function () { 140 | return ~~(this.milliseconds() / 100); 141 | }, 142 | SS : function () { 143 | return leftZeroFill(~~(this.milliseconds() / 10), 2); 144 | }, 145 | SSS : function () { 146 | return leftZeroFill(this.milliseconds(), 3); 147 | }, 148 | Z : function () { 149 | var a = -this.zone(), 150 | b = "+"; 151 | if (a < 0) { 152 | a = -a; 153 | b = "-"; 154 | } 155 | return b + leftZeroFill(~~(a / 60), 2) + ":" + leftZeroFill(~~a % 60, 2); 156 | }, 157 | ZZ : function () { 158 | var a = -this.zone(), 159 | b = "+"; 160 | if (a < 0) { 161 | a = -a; 162 | b = "-"; 163 | } 164 | return b + leftZeroFill(~~(10 * a / 6), 4); 165 | }, 166 | X : function () { 167 | return this.unix(); 168 | } 169 | }; 170 | 171 | function padToken(func, count) { 172 | return function (a) { 173 | return leftZeroFill(func.call(this, a), count); 174 | }; 175 | } 176 | function ordinalizeToken(func) { 177 | return function (a) { 178 | return this.lang().ordinal(func.call(this, a)); 179 | }; 180 | } 181 | 182 | while (ordinalizeTokens.length) { 183 | i = ordinalizeTokens.pop(); 184 | formatTokenFunctions[i + 'o'] = ordinalizeToken(formatTokenFunctions[i]); 185 | } 186 | while (paddedTokens.length) { 187 | i = paddedTokens.pop(); 188 | formatTokenFunctions[i + i] = padToken(formatTokenFunctions[i], 2); 189 | } 190 | formatTokenFunctions.DDDD = padToken(formatTokenFunctions.DDD, 3); 191 | 192 | 193 | /************************************ 194 | Constructors 195 | ************************************/ 196 | 197 | function Language() { 198 | 199 | } 200 | 201 | // Moment prototype object 202 | function Moment(config) { 203 | extend(this, config); 204 | } 205 | 206 | // Duration Constructor 207 | function Duration(duration) { 208 | var data = this._data = {}, 209 | years = duration.years || duration.year || duration.y || 0, 210 | months = duration.months || duration.month || duration.M || 0, 211 | weeks = duration.weeks || duration.week || duration.w || 0, 212 | days = duration.days || duration.day || duration.d || 0, 213 | hours = duration.hours || duration.hour || duration.h || 0, 214 | minutes = duration.minutes || duration.minute || duration.m || 0, 215 | seconds = duration.seconds || duration.second || duration.s || 0, 216 | milliseconds = duration.milliseconds || duration.millisecond || duration.ms || 0; 217 | 218 | // representation for dateAddRemove 219 | this._milliseconds = milliseconds + 220 | seconds * 1e3 + // 1000 221 | minutes * 6e4 + // 1000 * 60 222 | hours * 36e5; // 1000 * 60 * 60 223 | // Because of dateAddRemove treats 24 hours as different from a 224 | // day when working around DST, we need to store them separately 225 | this._days = days + 226 | weeks * 7; 227 | // It is impossible translate months into days without knowing 228 | // which months you are are talking about, so we have to store 229 | // it separately. 230 | this._months = months + 231 | years * 12; 232 | 233 | // The following code bubbles up values, see the tests for 234 | // examples of what that means. 235 | data.milliseconds = milliseconds % 1000; 236 | seconds += absRound(milliseconds / 1000); 237 | 238 | data.seconds = seconds % 60; 239 | minutes += absRound(seconds / 60); 240 | 241 | data.minutes = minutes % 60; 242 | hours += absRound(minutes / 60); 243 | 244 | data.hours = hours % 24; 245 | days += absRound(hours / 24); 246 | 247 | days += weeks * 7; 248 | data.days = days % 30; 249 | 250 | months += absRound(days / 30); 251 | 252 | data.months = months % 12; 253 | years += absRound(months / 12); 254 | 255 | data.years = years; 256 | } 257 | 258 | 259 | /************************************ 260 | Helpers 261 | ************************************/ 262 | 263 | 264 | function extend(a, b) { 265 | for (var i in b) { 266 | if (b.hasOwnProperty(i)) { 267 | a[i] = b[i]; 268 | } 269 | } 270 | return a; 271 | } 272 | 273 | function absRound(number) { 274 | if (number < 0) { 275 | return Math.ceil(number); 276 | } else { 277 | return Math.floor(number); 278 | } 279 | } 280 | 281 | // left zero fill a number 282 | // see http://jsperf.com/left-zero-filling for performance comparison 283 | function leftZeroFill(number, targetLength) { 284 | var output = number + ''; 285 | while (output.length < targetLength) { 286 | output = '0' + output; 287 | } 288 | return output; 289 | } 290 | 291 | // helper function for _.addTime and _.subtractTime 292 | function addOrSubtractDurationFromMoment(mom, duration, isAdding) { 293 | var ms = duration._milliseconds, 294 | d = duration._days, 295 | M = duration._months, 296 | currentDate; 297 | 298 | if (ms) { 299 | mom._d.setTime(+mom + ms * isAdding); 300 | } 301 | if (d) { 302 | mom.date(mom.date() + d * isAdding); 303 | } 304 | if (M) { 305 | currentDate = mom.date(); 306 | mom.date(1) 307 | .month(mom.month() + M * isAdding) 308 | .date(Math.min(currentDate, mom.daysInMonth())); 309 | } 310 | } 311 | 312 | // check if is an array 313 | function isArray(input) { 314 | return Object.prototype.toString.call(input) === '[object Array]'; 315 | } 316 | 317 | // compare two arrays, return the number of differences 318 | function compareArrays(array1, array2) { 319 | var len = Math.min(array1.length, array2.length), 320 | lengthDiff = Math.abs(array1.length - array2.length), 321 | diffs = 0, 322 | i; 323 | for (i = 0; i < len; i++) { 324 | if (~~array1[i] !== ~~array2[i]) { 325 | diffs++; 326 | } 327 | } 328 | return diffs + lengthDiff; 329 | } 330 | 331 | 332 | /************************************ 333 | Languages 334 | ************************************/ 335 | 336 | 337 | Language.prototype = { 338 | set : function (config) { 339 | var prop, i; 340 | for (i in config) { 341 | prop = config[i]; 342 | if (typeof prop === 'function') { 343 | this[i] = prop; 344 | } else { 345 | this['_' + i] = prop; 346 | } 347 | } 348 | }, 349 | 350 | _months : "January_February_March_April_May_June_July_August_September_October_November_December".split("_"), 351 | months : function (m) { 352 | return this._months[m.month()]; 353 | }, 354 | 355 | _monthsShort : "Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"), 356 | monthsShort : function (m) { 357 | return this._monthsShort[m.month()]; 358 | }, 359 | 360 | monthsParse : function (monthName) { 361 | var i, mom, regex, output; 362 | 363 | if (!this._monthsParse) { 364 | this._monthsParse = []; 365 | } 366 | 367 | for (i = 0; i < 12; i++) { 368 | // make the regex if we don't have it already 369 | if (!this._monthsParse[i]) { 370 | mom = moment([2000, i]); 371 | regex = '^' + this.months(mom, '') + '|^' + this.monthsShort(mom, ''); 372 | this._monthsParse[i] = new RegExp(regex.replace('.', ''), 'i'); 373 | } 374 | // test the regex 375 | if (this._monthsParse[i].test(monthName)) { 376 | return i; 377 | } 378 | } 379 | }, 380 | 381 | _weekdays : "Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"), 382 | weekdays : function (m) { 383 | return this._weekdays[m.day()]; 384 | }, 385 | 386 | _weekdaysShort : "Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"), 387 | weekdaysShort : function (m) { 388 | return this._weekdaysShort[m.day()]; 389 | }, 390 | 391 | _weekdaysMin : "Su_Mo_Tu_We_Th_Fr_Sa".split("_"), 392 | weekdaysMin : function (m) { 393 | return this._weekdaysMin[m.day()]; 394 | }, 395 | 396 | _longDateFormat : { 397 | LT : "h:mm A", 398 | L : "MM/DD/YYYY", 399 | LL : "MMMM D YYYY", 400 | LLL : "MMMM D YYYY LT", 401 | LLLL : "dddd, MMMM D YYYY LT" 402 | }, 403 | longDateFormat : function (key) { 404 | var output = this._longDateFormat[key]; 405 | if (!output && this._longDateFormat[key.toUpperCase()]) { 406 | output = this._longDateFormat[key.toUpperCase()].replace(/MMMM|MM|DD|dddd/g, function (val) { 407 | return val.slice(1); 408 | }); 409 | this._longDateFormat[key] = output; 410 | } 411 | return output; 412 | }, 413 | 414 | meridiem : function (hours, minutes, isLower) { 415 | if (hours > 11) { 416 | return isLower ? 'pm' : 'PM'; 417 | } else { 418 | return isLower ? 'am' : 'AM'; 419 | } 420 | }, 421 | 422 | _calendar : { 423 | sameDay : '[Today at] LT', 424 | nextDay : '[Tomorrow at] LT', 425 | nextWeek : 'dddd [at] LT', 426 | lastDay : '[Yesterday at] LT', 427 | lastWeek : '[last] dddd [at] LT', 428 | sameElse : 'L' 429 | }, 430 | calendar : function (key, mom) { 431 | var output = this._calendar[key]; 432 | return typeof output === 'function' ? output.apply(mom) : output; 433 | }, 434 | 435 | _relativeTime : { 436 | future : "in %s", 437 | past : "%s ago", 438 | s : "a few seconds", 439 | m : "a minute", 440 | mm : "%d minutes", 441 | h : "an hour", 442 | hh : "%d hours", 443 | d : "a day", 444 | dd : "%d days", 445 | M : "a month", 446 | MM : "%d months", 447 | y : "a year", 448 | yy : "%d years" 449 | }, 450 | relativeTime : function (number, withoutSuffix, string, isFuture) { 451 | var output = this._relativeTime[string]; 452 | return (typeof output === 'function') ? 453 | output(number, withoutSuffix, string, isFuture) : 454 | output.replace(/%d/i, number); 455 | }, 456 | pastFuture : function (diff, output) { 457 | var format = this._relativeTime[diff > 0 ? 'future' : 'past']; 458 | return typeof format === 'function' ? format(output) : format.replace(/%s/i, output); 459 | }, 460 | 461 | ordinal : function (number) { 462 | return this._ordinal.replace("%d", number); 463 | }, 464 | _ordinal : "%d", 465 | 466 | preparse : function (string) { 467 | return string; 468 | }, 469 | 470 | postformat : function (string) { 471 | return string; 472 | }, 473 | 474 | week : function (mom) { 475 | return weekOfYear(mom, this._week.dow, this._week.doy); 476 | }, 477 | _week : { 478 | dow : 0, // Sunday is the first day of the week. 479 | doy : 6 // The week that contains Jan 1st is the first week of the year. 480 | } 481 | }; 482 | 483 | // Loads a language definition into the `languages` cache. The function 484 | // takes a key and optionally values. If not in the browser and no values 485 | // are provided, it will load the language file module. As a convenience, 486 | // this function also returns the language values. 487 | function loadLang(key, values) { 488 | values.abbr = key; 489 | if (!languages[key]) { 490 | languages[key] = new Language(); 491 | } 492 | languages[key].set(values); 493 | return languages[key]; 494 | } 495 | 496 | // Determines which language definition to use and returns it. 497 | // 498 | // With no parameters, it will return the global language. If you 499 | // pass in a language key, such as 'en', it will return the 500 | // definition for 'en', so long as 'en' has already been loaded using 501 | // moment.lang. 502 | function getLangDefinition(key) { 503 | if (!key) { 504 | return moment.fn._lang; 505 | } 506 | if (!languages[key] && hasModule) { 507 | require('./lang/' + key); 508 | } 509 | return languages[key]; 510 | } 511 | 512 | 513 | /************************************ 514 | Formatting 515 | ************************************/ 516 | 517 | 518 | function removeFormattingTokens(input) { 519 | if (input.match(/\[.*\]/)) { 520 | return input.replace(/^\[|\]$/g, ""); 521 | } 522 | return input.replace(/\\/g, ""); 523 | } 524 | 525 | function makeFormatFunction(format) { 526 | var array = format.match(formattingTokens), i, length; 527 | 528 | for (i = 0, length = array.length; i < length; i++) { 529 | if (formatTokenFunctions[array[i]]) { 530 | array[i] = formatTokenFunctions[array[i]]; 531 | } else { 532 | array[i] = removeFormattingTokens(array[i]); 533 | } 534 | } 535 | 536 | return function (mom) { 537 | var output = ""; 538 | for (i = 0; i < length; i++) { 539 | output += typeof array[i].call === 'function' ? array[i].call(mom, format) : array[i]; 540 | } 541 | return output; 542 | }; 543 | } 544 | 545 | // format date using native date object 546 | function formatMoment(m, format) { 547 | var i = 5; 548 | 549 | function replaceLongDateFormatTokens(input) { 550 | return m.lang().longDateFormat(input) || input; 551 | } 552 | 553 | while (i-- && localFormattingTokens.test(format)) { 554 | format = format.replace(localFormattingTokens, replaceLongDateFormatTokens); 555 | } 556 | 557 | if (!formatFunctions[format]) { 558 | formatFunctions[format] = makeFormatFunction(format); 559 | } 560 | 561 | return formatFunctions[format](m); 562 | } 563 | 564 | 565 | /************************************ 566 | Parsing 567 | ************************************/ 568 | 569 | 570 | // get the regex to find the next token 571 | function getParseRegexForToken(token) { 572 | switch (token) { 573 | case 'DDDD': 574 | return parseTokenThreeDigits; 575 | case 'YYYY': 576 | return parseTokenFourDigits; 577 | case 'YYYYY': 578 | return parseTokenSixDigits; 579 | case 'S': 580 | case 'SS': 581 | case 'SSS': 582 | case 'DDD': 583 | return parseTokenOneToThreeDigits; 584 | case 'MMM': 585 | case 'MMMM': 586 | case 'dd': 587 | case 'ddd': 588 | case 'dddd': 589 | case 'a': 590 | case 'A': 591 | return parseTokenWord; 592 | case 'X': 593 | return parseTokenTimestampMs; 594 | case 'Z': 595 | case 'ZZ': 596 | return parseTokenTimezone; 597 | case 'T': 598 | return parseTokenT; 599 | case 'MM': 600 | case 'DD': 601 | case 'YY': 602 | case 'HH': 603 | case 'hh': 604 | case 'mm': 605 | case 'ss': 606 | case 'M': 607 | case 'D': 608 | case 'd': 609 | case 'H': 610 | case 'h': 611 | case 'm': 612 | case 's': 613 | return parseTokenOneOrTwoDigits; 614 | default : 615 | return new RegExp(token.replace('\\', '')); 616 | } 617 | } 618 | 619 | // function to convert string input to date 620 | function addTimeToArrayFromToken(token, input, config) { 621 | var a, b, 622 | datePartArray = config._a; 623 | 624 | switch (token) { 625 | // MONTH 626 | case 'M' : // fall through to MM 627 | case 'MM' : 628 | datePartArray[1] = (input == null) ? 0 : ~~input - 1; 629 | break; 630 | case 'MMM' : // fall through to MMMM 631 | case 'MMMM' : 632 | a = getLangDefinition(config._l).monthsParse(input); 633 | // if we didn't find a month name, mark the date as invalid. 634 | if (a != null) { 635 | datePartArray[1] = a; 636 | } else { 637 | config._isValid = false; 638 | } 639 | break; 640 | // DAY OF MONTH 641 | case 'D' : // fall through to DDDD 642 | case 'DD' : // fall through to DDDD 643 | case 'DDD' : // fall through to DDDD 644 | case 'DDDD' : 645 | if (input != null) { 646 | datePartArray[2] = ~~input; 647 | } 648 | break; 649 | // YEAR 650 | case 'YY' : 651 | datePartArray[0] = ~~input + (~~input > 68 ? 1900 : 2000); 652 | break; 653 | case 'YYYY' : 654 | case 'YYYYY' : 655 | datePartArray[0] = ~~input; 656 | break; 657 | // AM / PM 658 | case 'a' : // fall through to A 659 | case 'A' : 660 | config._isPm = ((input + '').toLowerCase() === 'pm'); 661 | break; 662 | // 24 HOUR 663 | case 'H' : // fall through to hh 664 | case 'HH' : // fall through to hh 665 | case 'h' : // fall through to hh 666 | case 'hh' : 667 | datePartArray[3] = ~~input; 668 | break; 669 | // MINUTE 670 | case 'm' : // fall through to mm 671 | case 'mm' : 672 | datePartArray[4] = ~~input; 673 | break; 674 | // SECOND 675 | case 's' : // fall through to ss 676 | case 'ss' : 677 | datePartArray[5] = ~~input; 678 | break; 679 | // MILLISECOND 680 | case 'S' : 681 | case 'SS' : 682 | case 'SSS' : 683 | datePartArray[6] = ~~ (('0.' + input) * 1000); 684 | break; 685 | // UNIX TIMESTAMP WITH MS 686 | case 'X': 687 | config._d = new Date(parseFloat(input) * 1000); 688 | break; 689 | // TIMEZONE 690 | case 'Z' : // fall through to ZZ 691 | case 'ZZ' : 692 | config._useUTC = true; 693 | a = (input + '').match(parseTimezoneChunker); 694 | if (a && a[1]) { 695 | config._tzh = ~~a[1]; 696 | } 697 | if (a && a[2]) { 698 | config._tzm = ~~a[2]; 699 | } 700 | // reverse offsets 701 | if (a && a[0] === '+') { 702 | config._tzh = -config._tzh; 703 | config._tzm = -config._tzm; 704 | } 705 | break; 706 | } 707 | 708 | // if the input is null, the date is not valid 709 | if (input == null) { 710 | config._isValid = false; 711 | } 712 | } 713 | 714 | // convert an array to a date. 715 | // the array should mirror the parameters below 716 | // note: all values past the year are optional and will default to the lowest possible value. 717 | // [year, month, day , hour, minute, second, millisecond] 718 | function dateFromArray(config) { 719 | var i, date, input = []; 720 | 721 | if (config._d) { 722 | return; 723 | } 724 | 725 | for (i = 0; i < 7; i++) { 726 | config._a[i] = input[i] = (config._a[i] == null) ? (i === 2 ? 1 : 0) : config._a[i]; 727 | } 728 | 729 | // add the offsets to the time to be parsed so that we can have a clean array for checking isValid 730 | input[3] += config._tzh || 0; 731 | input[4] += config._tzm || 0; 732 | 733 | date = new Date(0); 734 | 735 | if (config._useUTC) { 736 | date.setUTCFullYear(input[0], input[1], input[2]); 737 | date.setUTCHours(input[3], input[4], input[5], input[6]); 738 | } else { 739 | date.setFullYear(input[0], input[1], input[2]); 740 | date.setHours(input[3], input[4], input[5], input[6]); 741 | } 742 | 743 | config._d = date; 744 | } 745 | 746 | // date from string and format string 747 | function makeDateFromStringAndFormat(config) { 748 | // This array is used to make a Date, either with `new Date` or `Date.UTC` 749 | var tokens = config._f.match(formattingTokens), 750 | string = config._i, 751 | i, parsedInput; 752 | 753 | config._a = []; 754 | 755 | for (i = 0; i < tokens.length; i++) { 756 | parsedInput = (getParseRegexForToken(tokens[i]).exec(string) || [])[0]; 757 | if (parsedInput) { 758 | string = string.slice(string.indexOf(parsedInput) + parsedInput.length); 759 | } 760 | // don't parse if its not a known token 761 | if (formatTokenFunctions[tokens[i]]) { 762 | addTimeToArrayFromToken(tokens[i], parsedInput, config); 763 | } 764 | } 765 | // handle am pm 766 | if (config._isPm && config._a[3] < 12) { 767 | config._a[3] += 12; 768 | } 769 | // if is 12 am, change hours to 0 770 | if (config._isPm === false && config._a[3] === 12) { 771 | config._a[3] = 0; 772 | } 773 | // return 774 | dateFromArray(config); 775 | } 776 | 777 | // date from string and array of format strings 778 | function makeDateFromStringAndArray(config) { 779 | var tempConfig, 780 | tempMoment, 781 | bestMoment, 782 | 783 | scoreToBeat = 99, 784 | i, 785 | currentDate, 786 | currentScore; 787 | 788 | while (config._f.length) { 789 | tempConfig = extend({}, config); 790 | tempConfig._f = config._f.pop(); 791 | makeDateFromStringAndFormat(tempConfig); 792 | tempMoment = new Moment(tempConfig); 793 | 794 | if (tempMoment.isValid()) { 795 | bestMoment = tempMoment; 796 | break; 797 | } 798 | 799 | currentScore = compareArrays(tempConfig._a, tempMoment.toArray()); 800 | 801 | if (currentScore < scoreToBeat) { 802 | scoreToBeat = currentScore; 803 | bestMoment = tempMoment; 804 | } 805 | } 806 | 807 | extend(config, bestMoment); 808 | } 809 | 810 | // date from iso format 811 | function makeDateFromString(config) { 812 | var i, 813 | string = config._i; 814 | if (isoRegex.exec(string)) { 815 | config._f = 'YYYY-MM-DDT'; 816 | for (i = 0; i < 4; i++) { 817 | if (isoTimes[i][1].exec(string)) { 818 | config._f += isoTimes[i][0]; 819 | break; 820 | } 821 | } 822 | if (parseTokenTimezone.exec(string)) { 823 | config._f += " Z"; 824 | } 825 | makeDateFromStringAndFormat(config); 826 | } else { 827 | config._d = new Date(string); 828 | } 829 | } 830 | 831 | function makeDateFromInput(config) { 832 | var input = config._i, 833 | matched = aspNetJsonRegex.exec(input); 834 | 835 | if (input === undefined) { 836 | config._d = new Date(); 837 | } else if (matched) { 838 | config._d = new Date(+matched[1]); 839 | } else if (typeof input === 'string') { 840 | makeDateFromString(config); 841 | } else if (isArray(input)) { 842 | config._a = input.slice(0); 843 | dateFromArray(config); 844 | } else { 845 | config._d = input instanceof Date ? new Date(+input) : new Date(input); 846 | } 847 | } 848 | 849 | 850 | /************************************ 851 | Relative Time 852 | ************************************/ 853 | 854 | 855 | // helper function for moment.fn.from, moment.fn.fromNow, and moment.duration.fn.humanize 856 | function substituteTimeAgo(string, number, withoutSuffix, isFuture, lang) { 857 | return lang.relativeTime(number || 1, !!withoutSuffix, string, isFuture); 858 | } 859 | 860 | function relativeTime(milliseconds, withoutSuffix, lang) { 861 | var seconds = round(Math.abs(milliseconds) / 1000), 862 | minutes = round(seconds / 60), 863 | hours = round(minutes / 60), 864 | days = round(hours / 24), 865 | years = round(days / 365), 866 | args = seconds < 45 && ['s', seconds] || 867 | minutes === 1 && ['m'] || 868 | minutes < 45 && ['mm', minutes] || 869 | hours === 1 && ['h'] || 870 | hours < 22 && ['hh', hours] || 871 | days === 1 && ['d'] || 872 | days <= 25 && ['dd', days] || 873 | days <= 45 && ['M'] || 874 | days < 345 && ['MM', round(days / 30)] || 875 | years === 1 && ['y'] || ['yy', years]; 876 | args[2] = withoutSuffix; 877 | args[3] = milliseconds > 0; 878 | args[4] = lang; 879 | return substituteTimeAgo.apply({}, args); 880 | } 881 | 882 | 883 | /************************************ 884 | Week of Year 885 | ************************************/ 886 | 887 | 888 | // firstDayOfWeek 0 = sun, 6 = sat 889 | // the day of the week that starts the week 890 | // (usually sunday or monday) 891 | // firstDayOfWeekOfYear 0 = sun, 6 = sat 892 | // the first week is the week that contains the first 893 | // of this day of the week 894 | // (eg. ISO weeks use thursday (4)) 895 | function weekOfYear(mom, firstDayOfWeek, firstDayOfWeekOfYear) { 896 | var end = firstDayOfWeekOfYear - firstDayOfWeek, 897 | daysToDayOfWeek = firstDayOfWeekOfYear - mom.day(); 898 | 899 | 900 | if (daysToDayOfWeek > end) { 901 | daysToDayOfWeek -= 7; 902 | } 903 | 904 | if (daysToDayOfWeek < end - 7) { 905 | daysToDayOfWeek += 7; 906 | } 907 | 908 | return Math.ceil(moment(mom).add('d', daysToDayOfWeek).dayOfYear() / 7); 909 | } 910 | 911 | 912 | /************************************ 913 | Top Level Functions 914 | ************************************/ 915 | 916 | function makeMoment(config) { 917 | var input = config._i, 918 | format = config._f; 919 | 920 | if (input === null || input === '') { 921 | return null; 922 | } 923 | 924 | if (typeof input === 'string') { 925 | config._i = input = getLangDefinition().preparse(input); 926 | } 927 | 928 | if (moment.isMoment(input)) { 929 | config = extend({}, input); 930 | config._d = new Date(+input._d); 931 | } else if (format) { 932 | if (isArray(format)) { 933 | makeDateFromStringAndArray(config); 934 | } else { 935 | makeDateFromStringAndFormat(config); 936 | } 937 | } else { 938 | makeDateFromInput(config); 939 | } 940 | 941 | return new Moment(config); 942 | } 943 | 944 | moment = function (input, format, lang) { 945 | return makeMoment({ 946 | _i : input, 947 | _f : format, 948 | _l : lang, 949 | _isUTC : false 950 | }); 951 | }; 952 | 953 | // creating with utc 954 | moment.utc = function (input, format, lang) { 955 | return makeMoment({ 956 | _useUTC : true, 957 | _isUTC : true, 958 | _l : lang, 959 | _i : input, 960 | _f : format 961 | }); 962 | }; 963 | 964 | // creating with unix timestamp (in seconds) 965 | moment.unix = function (input) { 966 | return moment(input * 1000); 967 | }; 968 | 969 | // duration 970 | moment.duration = function (input, key) { 971 | var isDuration = moment.isDuration(input), 972 | isNumber = (typeof input === 'number'), 973 | duration = (isDuration ? input._data : (isNumber ? {} : input)), 974 | ret; 975 | 976 | if (isNumber) { 977 | if (key) { 978 | duration[key] = input; 979 | } else { 980 | duration.milliseconds = input; 981 | } 982 | } 983 | 984 | ret = new Duration(duration); 985 | 986 | if (isDuration && input.hasOwnProperty('_lang')) { 987 | ret._lang = input._lang; 988 | } 989 | 990 | return ret; 991 | }; 992 | 993 | // version number 994 | moment.version = VERSION; 995 | 996 | // default format 997 | moment.defaultFormat = isoFormat; 998 | 999 | // This function will load languages and then set the global language. If 1000 | // no arguments are passed in, it will simply return the current global 1001 | // language key. 1002 | moment.lang = function (key, values) { 1003 | var i; 1004 | 1005 | if (!key) { 1006 | return moment.fn._lang._abbr; 1007 | } 1008 | if (values) { 1009 | loadLang(key, values); 1010 | } else if (!languages[key]) { 1011 | getLangDefinition(key); 1012 | } 1013 | moment.duration.fn._lang = moment.fn._lang = getLangDefinition(key); 1014 | }; 1015 | 1016 | // returns language data 1017 | moment.langData = function (key) { 1018 | if (key && key._lang && key._lang._abbr) { 1019 | key = key._lang._abbr; 1020 | } 1021 | return getLangDefinition(key); 1022 | }; 1023 | 1024 | // compare moment object 1025 | moment.isMoment = function (obj) { 1026 | return obj instanceof Moment; 1027 | }; 1028 | 1029 | // for typechecking Duration objects 1030 | moment.isDuration = function (obj) { 1031 | return obj instanceof Duration; 1032 | }; 1033 | 1034 | 1035 | /************************************ 1036 | Moment Prototype 1037 | ************************************/ 1038 | 1039 | 1040 | moment.fn = Moment.prototype = { 1041 | 1042 | clone : function () { 1043 | return moment(this); 1044 | }, 1045 | 1046 | valueOf : function () { 1047 | return +this._d; 1048 | }, 1049 | 1050 | unix : function () { 1051 | return Math.floor(+this._d / 1000); 1052 | }, 1053 | 1054 | toString : function () { 1055 | return this.format("ddd MMM DD YYYY HH:mm:ss [GMT]ZZ"); 1056 | }, 1057 | 1058 | toDate : function () { 1059 | return this._d; 1060 | }, 1061 | 1062 | toJSON : function () { 1063 | return moment.utc(this).format('YYYY-MM-DD[T]HH:mm:ss.SSS[Z]'); 1064 | }, 1065 | 1066 | toArray : function () { 1067 | var m = this; 1068 | return [ 1069 | m.year(), 1070 | m.month(), 1071 | m.date(), 1072 | m.hours(), 1073 | m.minutes(), 1074 | m.seconds(), 1075 | m.milliseconds() 1076 | ]; 1077 | }, 1078 | 1079 | isValid : function () { 1080 | if (this._isValid == null) { 1081 | if (this._a) { 1082 | this._isValid = !compareArrays(this._a, (this._isUTC ? moment.utc(this._a) : moment(this._a)).toArray()); 1083 | } else { 1084 | this._isValid = !isNaN(this._d.getTime()); 1085 | } 1086 | } 1087 | return !!this._isValid; 1088 | }, 1089 | 1090 | utc : function () { 1091 | this._isUTC = true; 1092 | return this; 1093 | }, 1094 | 1095 | local : function () { 1096 | this._isUTC = false; 1097 | return this; 1098 | }, 1099 | 1100 | format : function (inputString) { 1101 | var output = formatMoment(this, inputString || moment.defaultFormat); 1102 | return this.lang().postformat(output); 1103 | }, 1104 | 1105 | add : function (input, val) { 1106 | var dur; 1107 | // switch args to support add('s', 1) and add(1, 's') 1108 | if (typeof input === 'string') { 1109 | dur = moment.duration(+val, input); 1110 | } else { 1111 | dur = moment.duration(input, val); 1112 | } 1113 | addOrSubtractDurationFromMoment(this, dur, 1); 1114 | return this; 1115 | }, 1116 | 1117 | subtract : function (input, val) { 1118 | var dur; 1119 | // switch args to support subtract('s', 1) and subtract(1, 's') 1120 | if (typeof input === 'string') { 1121 | dur = moment.duration(+val, input); 1122 | } else { 1123 | dur = moment.duration(input, val); 1124 | } 1125 | addOrSubtractDurationFromMoment(this, dur, -1); 1126 | return this; 1127 | }, 1128 | 1129 | diff : function (input, units, asFloat) { 1130 | var that = this._isUTC ? moment(input).utc() : moment(input).local(), 1131 | zoneDiff = (this.zone() - that.zone()) * 6e4, 1132 | diff, output; 1133 | 1134 | if (units) { 1135 | // standardize on singular form 1136 | units = units.replace(/s$/, ''); 1137 | } 1138 | 1139 | if (units === 'year' || units === 'month') { 1140 | diff = (this.daysInMonth() + that.daysInMonth()) * 432e5; // 24 * 60 * 60 * 1000 / 2 1141 | output = ((this.year() - that.year()) * 12) + (this.month() - that.month()); 1142 | output += ((this - moment(this).startOf('month')) - (that - moment(that).startOf('month'))) / diff; 1143 | if (units === 'year') { 1144 | output = output / 12; 1145 | } 1146 | } else { 1147 | diff = (this - that) - zoneDiff; 1148 | output = units === 'second' ? diff / 1e3 : // 1000 1149 | units === 'minute' ? diff / 6e4 : // 1000 * 60 1150 | units === 'hour' ? diff / 36e5 : // 1000 * 60 * 60 1151 | units === 'day' ? diff / 864e5 : // 1000 * 60 * 60 * 24 1152 | units === 'week' ? diff / 6048e5 : // 1000 * 60 * 60 * 24 * 7 1153 | diff; 1154 | } 1155 | return asFloat ? output : absRound(output); 1156 | }, 1157 | 1158 | from : function (time, withoutSuffix) { 1159 | return moment.duration(this.diff(time)).lang(this.lang()._abbr).humanize(!withoutSuffix); 1160 | }, 1161 | 1162 | fromNow : function (withoutSuffix) { 1163 | return this.from(moment(), withoutSuffix); 1164 | }, 1165 | 1166 | calendar : function () { 1167 | var diff = this.diff(moment().startOf('day'), 'days', true), 1168 | format = diff < -6 ? 'sameElse' : 1169 | diff < -1 ? 'lastWeek' : 1170 | diff < 0 ? 'lastDay' : 1171 | diff < 1 ? 'sameDay' : 1172 | diff < 2 ? 'nextDay' : 1173 | diff < 7 ? 'nextWeek' : 'sameElse'; 1174 | return this.format(this.lang().calendar(format, this)); 1175 | }, 1176 | 1177 | isLeapYear : function () { 1178 | var year = this.year(); 1179 | return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0; 1180 | }, 1181 | 1182 | isDST : function () { 1183 | return (this.zone() < moment([this.year()]).zone() || 1184 | this.zone() < moment([this.year(), 5]).zone()); 1185 | }, 1186 | 1187 | day : function (input) { 1188 | var day = this._isUTC ? this._d.getUTCDay() : this._d.getDay(); 1189 | return input == null ? day : 1190 | this.add({ d : input - day }); 1191 | }, 1192 | 1193 | startOf: function (units) { 1194 | units = units.replace(/s$/, ''); 1195 | // the following switch intentionally omits break keywords 1196 | // to utilize falling through the cases. 1197 | switch (units) { 1198 | case 'year': 1199 | this.month(0); 1200 | /* falls through */ 1201 | case 'month': 1202 | this.date(1); 1203 | /* falls through */ 1204 | case 'week': 1205 | case 'day': 1206 | this.hours(0); 1207 | /* falls through */ 1208 | case 'hour': 1209 | this.minutes(0); 1210 | /* falls through */ 1211 | case 'minute': 1212 | this.seconds(0); 1213 | /* falls through */ 1214 | case 'second': 1215 | this.milliseconds(0); 1216 | /* falls through */ 1217 | } 1218 | 1219 | // weeks are a special case 1220 | if (units === 'week') { 1221 | this.day(0); 1222 | } 1223 | 1224 | return this; 1225 | }, 1226 | 1227 | endOf: function (units) { 1228 | return this.startOf(units).add(units.replace(/s?$/, 's'), 1).subtract('ms', 1); 1229 | }, 1230 | 1231 | isAfter: function (input, units) { 1232 | units = typeof units !== 'undefined' ? units : 'millisecond'; 1233 | return +this.clone().startOf(units) > +moment(input).startOf(units); 1234 | }, 1235 | 1236 | isBefore: function (input, units) { 1237 | units = typeof units !== 'undefined' ? units : 'millisecond'; 1238 | return +this.clone().startOf(units) < +moment(input).startOf(units); 1239 | }, 1240 | 1241 | isSame: function (input, units) { 1242 | units = typeof units !== 'undefined' ? units : 'millisecond'; 1243 | return +this.clone().startOf(units) === +moment(input).startOf(units); 1244 | }, 1245 | 1246 | zone : function () { 1247 | return this._isUTC ? 0 : this._d.getTimezoneOffset(); 1248 | }, 1249 | 1250 | daysInMonth : function () { 1251 | return moment.utc([this.year(), this.month() + 1, 0]).date(); 1252 | }, 1253 | 1254 | dayOfYear : function (input) { 1255 | var dayOfYear = round((moment(this).startOf('day') - moment(this).startOf('year')) / 864e5) + 1; 1256 | return input == null ? dayOfYear : this.add("d", (input - dayOfYear)); 1257 | }, 1258 | 1259 | isoWeek : function (input) { 1260 | var week = weekOfYear(this, 1, 4); 1261 | return input == null ? week : this.add("d", (input - week) * 7); 1262 | }, 1263 | 1264 | week : function (input) { 1265 | var week = this.lang().week(this); 1266 | return input == null ? week : this.add("d", (input - week) * 7); 1267 | }, 1268 | 1269 | // If passed a language key, it will set the language for this 1270 | // instance. Otherwise, it will return the language configuration 1271 | // variables for this instance. 1272 | lang : function (key) { 1273 | if (key === undefined) { 1274 | return this._lang; 1275 | } else { 1276 | this._lang = getLangDefinition(key); 1277 | return this; 1278 | } 1279 | } 1280 | }; 1281 | 1282 | // helper for adding shortcuts 1283 | function makeGetterAndSetter(name, key) { 1284 | moment.fn[name] = moment.fn[name + 's'] = function (input) { 1285 | var utc = this._isUTC ? 'UTC' : ''; 1286 | if (input != null) { 1287 | this._d['set' + utc + key](input); 1288 | return this; 1289 | } else { 1290 | return this._d['get' + utc + key](); 1291 | } 1292 | }; 1293 | } 1294 | 1295 | // loop through and add shortcuts (Month, Date, Hours, Minutes, Seconds, Milliseconds) 1296 | for (i = 0; i < proxyGettersAndSetters.length; i ++) { 1297 | makeGetterAndSetter(proxyGettersAndSetters[i].toLowerCase().replace(/s$/, ''), proxyGettersAndSetters[i]); 1298 | } 1299 | 1300 | // add shortcut for year (uses different syntax than the getter/setter 'year' == 'FullYear') 1301 | makeGetterAndSetter('year', 'FullYear'); 1302 | 1303 | // add plural methods 1304 | moment.fn.days = moment.fn.day; 1305 | moment.fn.weeks = moment.fn.week; 1306 | moment.fn.isoWeeks = moment.fn.isoWeek; 1307 | 1308 | /************************************ 1309 | Duration Prototype 1310 | ************************************/ 1311 | 1312 | 1313 | moment.duration.fn = Duration.prototype = { 1314 | weeks : function () { 1315 | return absRound(this.days() / 7); 1316 | }, 1317 | 1318 | valueOf : function () { 1319 | return this._milliseconds + 1320 | this._days * 864e5 + 1321 | this._months * 2592e6; 1322 | }, 1323 | 1324 | humanize : function (withSuffix) { 1325 | var difference = +this, 1326 | output = relativeTime(difference, !withSuffix, this.lang()); 1327 | 1328 | if (withSuffix) { 1329 | output = this.lang().pastFuture(difference, output); 1330 | } 1331 | 1332 | return this.lang().postformat(output); 1333 | }, 1334 | 1335 | lang : moment.fn.lang 1336 | }; 1337 | 1338 | function makeDurationGetter(name) { 1339 | moment.duration.fn[name] = function () { 1340 | return this._data[name]; 1341 | }; 1342 | } 1343 | 1344 | function makeDurationAsGetter(name, factor) { 1345 | moment.duration.fn['as' + name] = function () { 1346 | return +this / factor; 1347 | }; 1348 | } 1349 | 1350 | for (i in unitMillisecondFactors) { 1351 | if (unitMillisecondFactors.hasOwnProperty(i)) { 1352 | makeDurationAsGetter(i, unitMillisecondFactors[i]); 1353 | makeDurationGetter(i.toLowerCase()); 1354 | } 1355 | } 1356 | 1357 | makeDurationAsGetter('Weeks', 6048e5); 1358 | 1359 | 1360 | /************************************ 1361 | Default Lang 1362 | ************************************/ 1363 | 1364 | 1365 | // Set default language, other languages will inherit from English. 1366 | moment.lang('en', { 1367 | ordinal : function (number) { 1368 | var b = number % 10, 1369 | output = (~~ (number % 100 / 10) === 1) ? 'th' : 1370 | (b === 1) ? 'st' : 1371 | (b === 2) ? 'nd' : 1372 | (b === 3) ? 'rd' : 'th'; 1373 | return number + output; 1374 | } 1375 | }); 1376 | 1377 | 1378 | /************************************ 1379 | Exposing Moment 1380 | ************************************/ 1381 | 1382 | 1383 | // CommonJS module is defined 1384 | if (hasModule) { 1385 | module.exports = moment; 1386 | } 1387 | /*global ender:false */ 1388 | if (typeof ender === 'undefined') { 1389 | // here, `this` means `window` in the browser, or `global` on the server 1390 | // add `moment` as a global object via a string identifier, 1391 | // for Closure Compiler "advanced" mode 1392 | this['moment'] = moment; 1393 | } 1394 | /*global define:false */ 1395 | if (typeof define === "function" && define.amd) { 1396 | define("moment", [], function () { 1397 | return moment; 1398 | }); 1399 | } 1400 | }).call(this); 1401 | -------------------------------------------------------------------------------- /lib/moment.min.gm.js: -------------------------------------------------------------------------------- 1 | // moment.js 2 | // version : 2.0.0 3 | // author : Tim Wood 4 | // license : MIT 5 | // momentjs.com 6 | // MODIFIED FOR USE IN GREASEMONKEY ENVIRONMENT 7 | (function(e){function O(e,t){return function(n){return j(e.call(this,n),t)}}function M(e){return function(t){return this.lang().ordinal(e.call(this,t))}}function _(){}function D(e){H(this,e)}function P(e){var t=this._data={},n=e.years||e.year||e.y||0,r=e.months||e.month||e.M||0,i=e.weeks||e.week||e.w||0,s=e.days||e.day||e.d||0,o=e.hours||e.hour||e.h||0,u=e.minutes||e.minute||e.m||0,a=e.seconds||e.second||e.s||0,f=e.milliseconds||e.millisecond||e.ms||0;this._milliseconds=f+a*1e3+u*6e4+o*36e5,this._days=s+i*7,this._months=r+n*12,t.milliseconds=f%1e3,a+=B(f/1e3),t.seconds=a%60,u+=B(a/60),t.minutes=u%60,o+=B(u/60),t.hours=o%24,s+=B(o/24),s+=i*7,t.days=s%30,r+=B(s/30),t.months=r%12,n+=B(r/12),t.years=n}function H(e,t){for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n]);return e}function B(e){return e<0?Math.ceil(e):Math.floor(e)}function j(e,t){var n=e+"";while(n.length68?1900:2e3);break;case"YYYY":case"YYYYY":s[0]=~~t;break;case"a":case"A":n._isPm=(t+"").toLowerCase()==="pm";break;case"H":case"HH":case"h":case"hh":s[3]=~~t;break;case"m":case"mm":s[4]=~~t;break;case"s":case"ss":s[5]=~~t;break;case"S":case"SS":case"SSS":s[6]=~~(("0."+t)*1e3);break;case"X":n._d=new Date(parseFloat(t)*1e3);break;case"Z":case"ZZ":n._useUTC=!0,r=(t+"").match(x),r&&r[1]&&(n._tzh=~~r[1]),r&&r[2]&&(n._tzm=~~r[2]),r&&r[0]==="+"&&(n._tzh=-n._tzh,n._tzm=-n._tzm)}t==null&&(n._isValid=!1)}function J(e){var t,n,r=[];if(e._d)return;for(t=0;t<7;t++)e._a[t]=r[t]=e._a[t]==null?t===2?1:0:e._a[t];r[3]+=e._tzh||0,r[4]+=e._tzm||0,n=new Date(0),e._useUTC?(n.setUTCFullYear(r[0],r[1],r[2]),n.setUTCHours(r[3],r[4],r[5],r[6])):(n.setFullYear(r[0],r[1],r[2]),n.setHours(r[3],r[4],r[5],r[6])),e._d=n}function K(e){var t=e._f.match(a),n=e._i,r,i;e._a=[];for(r=0;r0,f[4]=n,Z.apply({},f)}function tt(e,n,r){var i=r-n,s=r-e.day();return s>i&&(s-=7),s11?n?"pm":"PM":n?"am":"AM"},_calendar:{sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[last] dddd [at] LT",sameElse:"L"},calendar:function(e,t){var n=this._calendar[e];return typeof n=="function"?n.apply(t):n},_relativeTime:{future:"in %s",past:"%s ago",s:"a few seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",M:"a month",MM:"%d months",y:"a year",yy:"%d years"},relativeTime:function(e,t,n,r){var i=this._relativeTime[n];return typeof i=="function"?i(e,t,n,r):i.replace(/%d/i,e)},pastFuture:function(e,t){var n=this._relativeTime[e>0?"future":"past"];return typeof n=="function"?n(t):n.replace(/%s/i,t)},ordinal:function(e){return this._ordinal.replace("%d",e)},_ordinal:"%d",preparse:function(e){return e},postformat:function(e){return e},week:function(e){return tt(e,this._week.dow,this._week.doy)},_week:{dow:0,doy:6}},t=function(e,t,n){return nt({_i:e,_f:t,_l:n,_isUTC:!1})},t.utc=function(e,t,n){return nt({_useUTC:!0,_isUTC:!0,_l:n,_i:e,_f:t})},t.unix=function(e){return t(e*1e3)},t.duration=function(e,n){var r=t.isDuration(e),i=typeof e=="number",s=r?e._data:i?{}:e,o;return i&&(n?s[n]=e:s.milliseconds=e),o=new P(s),r&&e.hasOwnProperty("_lang")&&(o._lang=e._lang),o},t.version=n,t.defaultFormat=E,t.lang=function(e,n){var r;if(!e)return t.fn._lang._abbr;n?R(e,n):s[e]||U(e),t.duration.fn._lang=t.fn._lang=U(e)},t.langData=function(e){return e&&e._lang&&e._lang._abbr&&(e=e._lang._abbr),U(e)},t.isMoment=function(e){return e instanceof D},t.isDuration=function(e){return e instanceof P},t.fn=D.prototype={clone:function(){return t(this)},valueOf:function(){return+this._d},unix:function(){return Math.floor(+this._d/1e3)},toString:function(){return this.format("ddd MMM DD YYYY HH:mm:ss [GMT]ZZ")},toDate:function(){return this._d},toJSON:function(){return t.utc(this).format("YYYY-MM-DD[T]HH:mm:ss.SSS[Z]")},toArray:function(){var e=this;return[e.year(),e.month(),e.date(),e.hours(),e.minutes(),e.seconds(),e.milliseconds()]},isValid:function(){return this._isValid==null&&(this._a?this._isValid=!q(this._a,(this._isUTC?t.utc(this._a):t(this._a)).toArray()):this._isValid=!isNaN(this._d.getTime())),!!this._isValid},utc:function(){return this._isUTC=!0,this},local:function(){return this._isUTC=!1,this},format:function(e){var n=X(this,e||t.defaultFormat);return this.lang().postformat(n)},add:function(e,n){var r;return typeof e=="string"?r=t.duration(+n,e):r=t.duration(e,n),F(this,r,1),this},subtract:function(e,n){var r;return typeof e=="string"?r=t.duration(+n,e):r=t.duration(e,n),F(this,r,-1),this},diff:function(e,n,r){var i=this._isUTC?t(e).utc():t(e).local(),s=(this.zone()-i.zone())*6e4,o,u;return n&&(n=n.replace(/s$/,"")),n==="year"||n==="month"?(o=(this.daysInMonth()+i.daysInMonth())*432e5,u=(this.year()-i.year())*12+(this.month()-i.month()),u+=(this-t(this).startOf("month")-(i-t(i).startOf("month")))/o,n==="year"&&(u/=12)):(o=this-i-s,u=n==="second"?o/1e3:n==="minute"?o/6e4:n==="hour"?o/36e5:n==="day"?o/864e5:n==="week"?o/6048e5:o),r?u:B(u)},from:function(e,n){return t.duration(this.diff(e)).lang(this.lang()._abbr).humanize(!n)},fromNow:function(e){return this.from(t(),e)},calendar:function(){var e=this.diff(t().startOf("day"),"days",!0),n=e<-6?"sameElse":e<-1?"lastWeek":e<0?"lastDay":e<1?"sameDay":e<2?"nextDay":e<7?"nextWeek":"sameElse";return this.format(this.lang().calendar(n,this))},isLeapYear:function(){var e=this.year();return e%4===0&&e%100!==0||e%400===0},isDST:function(){return this.zone()+t(e).startOf(n)},isBefore:function(e,n){return n=typeof n!="undefined"?n:"millisecond",+this.clone().startOf(n)<+t(e).startOf(n)},isSame:function(e,n){return n=typeof n!="undefined"?n:"millisecond",+this.clone().startOf(n)===+t(e).startOf(n)},zone:function(){return this._isUTC?0:this._d.getTimezoneOffset()},daysInMonth:function(){return t.utc([this.year(),this.month()+1,0]).date()},dayOfYear:function(e){var n=r((t(this).startOf("day")-t(this).startOf("year"))/864e5)+1;return e==null?n:this.add("d",e-n)},isoWeek:function(e){var t=tt(this,1,4);return e==null?t:this.add("d",(e-t)*7)},week:function(e){var t=this.lang().week(this);return e==null?t:this.add("d",(e-t)*7)},lang:function(t){return t===e?this._lang:(this._lang=U(t),this)}};for(i=0;i68?1900:2e3);break;case"YYYY":case"YYYYY":s[0]=~~t;break;case"a":case"A":n._isPm=(t+"").toLowerCase()==="pm";break;case"H":case"HH":case"h":case"hh":s[3]=~~t;break;case"m":case"mm":s[4]=~~t;break;case"s":case"ss":s[5]=~~t;break;case"S":case"SS":case"SSS":s[6]=~~(("0."+t)*1e3);break;case"X":n._d=new Date(parseFloat(t)*1e3);break;case"Z":case"ZZ":n._useUTC=!0,r=(t+"").match(x),r&&r[1]&&(n._tzh=~~r[1]),r&&r[2]&&(n._tzm=~~r[2]),r&&r[0]==="+"&&(n._tzh=-n._tzh,n._tzm=-n._tzm)}t==null&&(n._isValid=!1)}function J(e){var t,n,r=[];if(e._d)return;for(t=0;t<7;t++)e._a[t]=r[t]=e._a[t]==null?t===2?1:0:e._a[t];r[3]+=e._tzh||0,r[4]+=e._tzm||0,n=new Date(0),e._useUTC?(n.setUTCFullYear(r[0],r[1],r[2]),n.setUTCHours(r[3],r[4],r[5],r[6])):(n.setFullYear(r[0],r[1],r[2]),n.setHours(r[3],r[4],r[5],r[6])),e._d=n}function K(e){var t=e._f.match(a),n=e._i,r,i;e._a=[];for(r=0;r0,f[4]=n,Z.apply({},f)}function tt(e,n,r){var i=r-n,s=r-e.day();return s>i&&(s-=7),s11?n?"pm":"PM":n?"am":"AM"},_calendar:{sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[last] dddd [at] LT",sameElse:"L"},calendar:function(e,t){var n=this._calendar[e];return typeof n=="function"?n.apply(t):n},_relativeTime:{future:"in %s",past:"%s ago",s:"a few seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",M:"a month",MM:"%d months",y:"a year",yy:"%d years"},relativeTime:function(e,t,n,r){var i=this._relativeTime[n];return typeof i=="function"?i(e,t,n,r):i.replace(/%d/i,e)},pastFuture:function(e,t){var n=this._relativeTime[e>0?"future":"past"];return typeof n=="function"?n(t):n.replace(/%s/i,t)},ordinal:function(e){return this._ordinal.replace("%d",e)},_ordinal:"%d",preparse:function(e){return e},postformat:function(e){return e},week:function(e){return tt(e,this._week.dow,this._week.doy)},_week:{dow:0,doy:6}},t=function(e,t,n){return nt({_i:e,_f:t,_l:n,_isUTC:!1})},t.utc=function(e,t,n){return nt({_useUTC:!0,_isUTC:!0,_l:n,_i:e,_f:t})},t.unix=function(e){return t(e*1e3)},t.duration=function(e,n){var r=t.isDuration(e),i=typeof e=="number",s=r?e._data:i?{}:e,o;return i&&(n?s[n]=e:s.milliseconds=e),o=new P(s),r&&e.hasOwnProperty("_lang")&&(o._lang=e._lang),o},t.version=n,t.defaultFormat=E,t.lang=function(e,n){var r;if(!e)return t.fn._lang._abbr;n?R(e,n):s[e]||U(e),t.duration.fn._lang=t.fn._lang=U(e)},t.langData=function(e){return e&&e._lang&&e._lang._abbr&&(e=e._lang._abbr),U(e)},t.isMoment=function(e){return e instanceof D},t.isDuration=function(e){return e instanceof P},t.fn=D.prototype={clone:function(){return t(this)},valueOf:function(){return+this._d},unix:function(){return Math.floor(+this._d/1e3)},toString:function(){return this.format("ddd MMM DD YYYY HH:mm:ss [GMT]ZZ")},toDate:function(){return this._d},toJSON:function(){return t.utc(this).format("YYYY-MM-DD[T]HH:mm:ss.SSS[Z]")},toArray:function(){var e=this;return[e.year(),e.month(),e.date(),e.hours(),e.minutes(),e.seconds(),e.milliseconds()]},isValid:function(){return this._isValid==null&&(this._a?this._isValid=!q(this._a,(this._isUTC?t.utc(this._a):t(this._a)).toArray()):this._isValid=!isNaN(this._d.getTime())),!!this._isValid},utc:function(){return this._isUTC=!0,this},local:function(){return this._isUTC=!1,this},format:function(e){var n=X(this,e||t.defaultFormat);return this.lang().postformat(n)},add:function(e,n){var r;return typeof e=="string"?r=t.duration(+n,e):r=t.duration(e,n),F(this,r,1),this},subtract:function(e,n){var r;return typeof e=="string"?r=t.duration(+n,e):r=t.duration(e,n),F(this,r,-1),this},diff:function(e,n,r){var i=this._isUTC?t(e).utc():t(e).local(),s=(this.zone()-i.zone())*6e4,o,u;return n&&(n=n.replace(/s$/,"")),n==="year"||n==="month"?(o=(this.daysInMonth()+i.daysInMonth())*432e5,u=(this.year()-i.year())*12+(this.month()-i.month()),u+=(this-t(this).startOf("month")-(i-t(i).startOf("month")))/o,n==="year"&&(u/=12)):(o=this-i-s,u=n==="second"?o/1e3:n==="minute"?o/6e4:n==="hour"?o/36e5:n==="day"?o/864e5:n==="week"?o/6048e5:o),r?u:B(u)},from:function(e,n){return t.duration(this.diff(e)).lang(this.lang()._abbr).humanize(!n)},fromNow:function(e){return this.from(t(),e)},calendar:function(){var e=this.diff(t().startOf("day"),"days",!0),n=e<-6?"sameElse":e<-1?"lastWeek":e<0?"lastDay":e<1?"sameDay":e<2?"nextDay":e<7?"nextWeek":"sameElse";return this.format(this.lang().calendar(n,this))},isLeapYear:function(){var e=this.year();return e%4===0&&e%100!==0||e%400===0},isDST:function(){return this.zone()+t(e).startOf(n)},isBefore:function(e,n){return n=typeof n!="undefined"?n:"millisecond",+this.clone().startOf(n)<+t(e).startOf(n)},isSame:function(e,n){return n=typeof n!="undefined"?n:"millisecond",+this.clone().startOf(n)===+t(e).startOf(n)},zone:function(){return this._isUTC?0:this._d.getTimezoneOffset()},daysInMonth:function(){return t.utc([this.year(),this.month()+1,0]).date()},dayOfYear:function(e){var n=r((t(this).startOf("day")-t(this).startOf("year"))/864e5)+1;return e==null?n:this.add("d",e-n)},isoWeek:function(e){var t=tt(this,1,4);return e==null?t:this.add("d",(e-t)*7)},week:function(e){var t=this.lang().week(this);return e==null?t:this.add("d",(e-t)*7)},lang:function(t){return t===e?this._lang:(this._lang=U(t),this)}};for(i=0;i= 0.8.0" 24 | }, 25 | "scripts": { 26 | }, 27 | "devDependencies": { 28 | "iced-coffee-script": "~1.6.0", 29 | "grunt-contrib-concat": "~0.1.3", 30 | "grunt-contrib-watch": "~0.2.0", 31 | "grunt-contrib-clean": "~0.4.0", 32 | "grunt": "~0.4.1" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # THUCAL2: Smart curriculum exporter for Tsinghua University 2 | 3 | **Download**: https://github.com/summivox/thucal2/raw/master/dist/thucal2.user.js 4 | 5 | Surprisingly it still works TM, years after I (summivox) graduated, despite rumored complete overhaul to the whole website (which of course has become vaporware). 6 | 7 | ## Screenshot 8 | 9 | ![][result] 10 | 11 | ## Main Features 12 | 13 | * Fully automated operation: only one click is needed 14 | * Needs no "origin date" input 15 | * Automatically saves exported calendar (through HTML5 Blob) 16 | * Accurate results 17 | * Irregular timing (`09:50-12:15`, labs, ...) 18 | * Lab details (lab content, location, ...) 19 | * Standard iCalendar format for import into popular calendar apps 20 | * All processing done locally for the sake of privacy 21 | 22 | ## Build 23 | 24 | THUCAL2 is built using [GRUNT](http://gruntjs.com). 25 | 26 | ``` 27 | npm install --global grunt-cli 28 | npm install 29 | grunt release 30 | ``` 31 | 32 | Use `dist/thucal2.user.js` 33 | 34 | ## Usage 35 | 36 | 1. Log into http://info.tsinghua.edu.cn/ 37 | ![][step1-1] 38 | 39 | 2. Click `THUCAL` 40 | ![][step2-1] 41 | 42 | 3. Successful: `.ics` file downloaded (file could be unnamed in FireFox due to browser restrictions) 43 | ![][step3-1] 44 | 45 | 4. Import file into calendar app 46 | ![][step4-1] 47 | 48 | ## License 49 | 50 | MIT 51 | 52 | [step1-1]: http://i.imgur.com/iycvWRo.png 53 | [step2-1]: http://i.imgur.com/1SNBYd7.png 54 | [step3-1]: http://i.imgur.com/IhH4vu0.png 55 | [step4-1]: http://i.imgur.com/6oMMJqy.png 56 | [result]: http://i.imgur.com/96uOClz.png 57 | -------------------------------------------------------------------------------- /renren.md: -------------------------------------------------------------------------------- 1 | # [THUCAL2][repo] 2 | 3 | ### 最常见问题:受网络学堂信息限制,每学期第一周星期一开始可一用!!!(实测星期天晚上应该就可以了~) 4 | 5 | 最新版本: 0.4.0 6 | 7 | 8 | 9 | ##特点 10 | 11 | * 完美解决各种不规则时间课程问题(指定时间的实验课,1大节3小节课……) 12 | * 直接生成iCalendar文件,方便导入各种日历app(Google日历,Hotmail日历,苹果日历……) 13 | * 所有导出工作在浏览器中完成 14 | * 同时支持本科生和研究生 15 | 16 | ##FAQ 17 | 18 | * Q: 提示`list错误:检查是否已登录http://info.tsinghua.edu.cn/` 19 | A: 照做。 20 | > 原因:info自身session管理混乱 21 | * Q: 提示`分析错误` 22 | A: 有多种情况: 23 | 1. THUCAL2只有在【学期开始之前半天】开始才可以使用(info在此之前还是上个学期的模式,信息不全) 24 | 2. 插件冲突。请检查 TM/GM/本插件 均已更新至最新版,并关闭其他可能在info上运行的插件再试 25 | * Q: 选课时间过了怎么办? 26 | A: 点击 [https://zhjwxk.cic.tsinghua.edu.cn/xklogin.do](https://zhjwxk.cic.tsinghua.edu.cn/xklogin.do) 27 | 28 | 或者:登录info,右侧【选课信息】=>【进入选课】,如图: 29 | ![][zhjw] 30 | 31 | 32 | ##效果 33 | 34 | ![][result] 35 | 36 | ##下载 安装 37 | 38 | * Chrome:先安装[TamperMonkey][]环境 39 | * Firefox: 先安装[GreaseMonkey][]环境 40 | * Mac/Safari:先安装[NinjaKit][]环境 **(需要手动安装脚本,见NinjaKit说明)** 41 | * ~~其他: 换Chrome吧少年!~~ 42 | 43 | [TamperMonkey]: https://chrome.google.com/webstore/detail/tampermonkey/dhdgffkkebhmkfjojejmpbldmpobfkfo 44 | [NinjaKit]: http://ss-o.net/safari/extension/NinjaKit.safariextz 45 | [GreaseKit]: http://8-p.info/greasekit/ 46 | [GreaseMonkey]: https://addons.mozilla.org/en-US/firefox/addon/greasemonkey/ 47 | 48 | 安装完成后,即可[下载安装本userscript](https://github.com/summivox/thucal2/raw/master/dist/thucal2.user.js): 49 | https://github.com/summivox/thucal2/raw/master/dist/thucal2.user.js 50 | 51 | ##使用方法(以本科生为例,研究生类似) 52 | 53 | **听说info界面更新了……下面的截图还是旧版的,操作没区别** 54 | 55 | 1. 登录info,打开选课系统,点`整体课表` 56 | ![][step1-1] 57 | 58 | 2. 点右上角多出的`THUCAL`按钮 59 | ![][step2-1] 60 | 61 | 3. 下方出现提示,若成功,浏览器会下载一个`thucal-xxxx-xxxx-x.ics`文件(FireFox可能会出现其他的名字,不要紧),即为导出的日历 62 | ![][step3-1] 63 | 64 | 4. 将该文件导入支持iCalendar格式的日历app中。如:[Google Calendar导入日历][gcal1]。 65 | ![][step4-1] 66 | 67 | ##问题反馈 68 | 69 | 大家使用中如果遇到什么问题,欢迎在原日志下或[github][issue]与我讨论~ 70 | 71 | [summivox]: https://github.com/summivox 72 | [repo]: https://github.com/summivox/thucal2 73 | [issue]: https://github.com/summivox/thucal2/issues 74 | 75 | 76 | [gcal1]: http://support.google.com/calendar/bin/answer.py?hl=zh-Hans&answer=83126 77 | 78 | [zhjw]: http://i.imgur.com/NoebEdw.png 79 | [step1-1]: http://i.imgur.com/OxO5RMg.png 80 | [step2-1]: http://i.imgur.com/SXHWGwW.png 81 | [step3-1]: http://i.imgur.com/IhH4vu0.png 82 | [step4-1]: http://i.imgur.com/6oMMJqy.png 83 | [result]: http://i.imgur.com/96uOClz.png 84 | -------------------------------------------------------------------------------- /screenshot/thucal2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/summivox/thucal2/a264c28bfcf8fe33bf3d6c90a25f88bcf32dd2c8/screenshot/thucal2.png -------------------------------------------------------------------------------- /screenshot/thucal2_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/summivox/thucal2/a264c28bfcf8fe33bf3d6c90a25f88bcf32dd2c8/screenshot/thucal2_1.png -------------------------------------------------------------------------------- /screenshot/thucal2_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/summivox/thucal2/a264c28bfcf8fe33bf3d6c90a25f88bcf32dd2c8/screenshot/thucal2_2.png -------------------------------------------------------------------------------- /screenshot/thucal2_3'.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/summivox/thucal2/a264c28bfcf8fe33bf3d6c90a25f88bcf32dd2c8/screenshot/thucal2_3'.png -------------------------------------------------------------------------------- /screenshot/thucal2_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/summivox/thucal2/a264c28bfcf8fe33bf3d6c90a25f88bcf32dd2c8/screenshot/thucal2_3.png -------------------------------------------------------------------------------- /screenshot/thucal2_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/summivox/thucal2/a264c28bfcf8fe33bf3d6c90a25f88bcf32dd2c8/screenshot/thucal2_4.png -------------------------------------------------------------------------------- /src/thucal2.iced: -------------------------------------------------------------------------------- 1 | ############ 2 | ## common 3 | buildArray=(dims...)-> 4 | if !(dims?.length) then return null 5 | d=dims?.shift() 6 | if d.length? 7 | a=[] 8 | for i in d 9 | a[i]=buildArray(dims...) 10 | else 11 | a=new Array(d) 12 | for i in [0...d] by 1 13 | a[i]=buildArray(dims...) 14 | a 15 | 16 | cmp=(a, b)-> 17 | switch 18 | when ab then +1 20 | else 0 21 | 22 | inferYear=(termIdP, m, d)-> 23 | md=moment().month(m-1).date(d) 24 | th=moment().month(5-1).date(1) 25 | if termIdP.termN==1 && md.isAfter th 26 | y=termIdP.beginY 27 | else 28 | y=termIdP.endY 29 | moment([y, m-1, d]) 30 | 31 | getTOffset=(t)->t.clone().diff(t.clone().startOf('day')) 32 | 33 | period=[ 34 | "00:00" # placeholder 35 | "08:00" #1 36 | "09:50" #2 37 | "13:30" #3 38 | "15:20" #4 39 | "17:05" #5 40 | "19:20" #6 41 | ].map((s)-> 42 | t=moment(s, 'HHmm') 43 | { 44 | beginT: getTOffset(t) 45 | endT : getTOffset(t.add(1, 'hours').add(35, 'minutes')) 46 | } 47 | ) 48 | 49 | parseTermId=(termId)-> 50 | if !(match=/(\d{4})-(\d{4})-(\d)/.exec termId)? then return null 51 | { 52 | beginY: parseInt match[1] 53 | endY : parseInt match[2] 54 | termN : parseInt match[3] 55 | } 56 | 57 | printTermId=(termIdP)-> 58 | termIdP.beginY+'-'+termIdP.endY+'-'+( 59 | switch termIdP.termN 60 | when 1 then '秋' 61 | when 2 then '春' 62 | else '不科学' 63 | ) 64 | 65 | parseTimeStr=(infoStr)-> 66 | if !(match=/时间(\d{1,2}:\d{1,2})-(\d{1,2}:\d{1,2})/.exec(infoStr))? then return null 67 | { 68 | beginT: getTOffset(moment(match[1], 'HHmm')) 69 | endT : getTOffset(moment(match[2], 'HHmm')) 70 | } 71 | 72 | parseWeekStr=(weekStr)-> 73 | if !(part=/(([\d,-]+)|全|前八|后八|单|双)周/.exec(weekStr)) then return null 74 | switch part[1].charAt(0) 75 | when '全' then return [1..16] 76 | when '前' then return [1..8] 77 | when '后' then return [9..16] 78 | when '单' then return (w for w in [1..16] by 2) 79 | when '双' then return (w for w in [2..16] by 2) 80 | else 81 | ret=[] 82 | for s in part[2].split(',') 83 | #isolated? 84 | if (match=/^\d+$/.exec(s))? 85 | ret.push(Number(match[0])) 86 | #range? 87 | if (match=/^(\d+)-(\d+)$/.exec(s))? 88 | ret.push(w) for w in [Number(match[1])..Number(match[2])] by 1 89 | ret 90 | 91 | 92 | ############ 93 | ## parse_L 94 | parse_L=(root, termIdP)-> 95 | days=root.find('td.doc_title').map(-> 96 | if !(match=/(\d{4})-(\d{2})-(\d{2})/.exec @innerHTML)? then throw Error @innerHTML 97 | #md=inferYear(termIdP, parseInt(match[1]), parseInt(match[2])) 98 | matchN = match[1..].map Number 99 | --matchN[1] # moment [yyyy, MM-1, dd] 100 | md = moment matchN 101 | ) 102 | tables=root.find('> table.data_list_table') 103 | for ymd, i in days 104 | items=$(tables[i]).find('tr:not(.data_list_title)').map(-> 105 | fields=$(this).find('td').map(->@innerHTML) 106 | { 107 | beginT: getTOffset(moment(fields[0], 'HHmm')) 108 | endT : getTOffset(moment(fields[1], 'HHmm')) 109 | cat : fields[2] 110 | name : fields[3] 111 | loc : fields[4] 112 | } 113 | ) 114 | {ymd, items} 115 | 116 | 117 | ############ 118 | ## parse_G 119 | parseRegLoc=(infoStr)-> 120 | infoStr.match(/// 121 | [\(;] # last delimiter 122 | ( #1 123 | [^\(\);]+ # location (no delimeter) 124 | ) 125 | \)$ 126 | ///)?[1] 127 | 128 | # lab information: name, location 129 | # beware nasty cases: 130 | # name(loc(loc);...) 131 | # (name)name( loc;...) 132 | # use manual parsing 133 | parseLabInfo=(infoStr)-> 134 | #locate parens matching last 135 | i=infoStr.lastIndexOf(')') 136 | if i==-1 then return {labName: '', loc: ''} 137 | n=1 138 | while n && i>0 139 | switch infoStr[--i] 140 | when '(' then n-- 141 | when ')' then n++ 142 | if i==0 then return {labName: '', loc: ''} 143 | { 144 | labName: infoStr[0...i] || '' 145 | loc: infoStr[i+1..].match(/([^;]*);/)?[1]?.trim() || '' 146 | } 147 | 148 | parse_G=(root)-> 149 | Gr=buildArray([1..7], [1..6]) 150 | Gl=buildArray([1..7], [1..6]) 151 | for z in [1..7] by 1 152 | for p in [1..6] by 1 153 | Gr[z][p]=[] 154 | Gl[z][p]=[] 155 | cell=$("#a#{p}_#{z}") 156 | 157 | # regular 158 | cell.find('a.mainHref').each(-> 159 | infoStr=@nextSibling.data.trim() 160 | loc=parseRegLoc(infoStr) || '' 161 | Gr[z][p].push { 162 | name : @textContent.trim() 163 | infoStr 164 | loc 165 | labName : '' 166 | week : parseWeekStr(infoStr) 167 | beginT : null # default 168 | endT : null 169 | } 170 | ) 171 | 172 | # lab 173 | cell.find('a.blue_red_none').each(-> 174 | infoStr=@nextElementSibling.textContent.trim() 175 | {labName, loc}=parseLabInfo(infoStr) 176 | if (t=parseTimeStr(infoStr))? 177 | {beginT, endT}=t 178 | else 179 | beginT=endT=null # default 180 | Gl[z][p].push { 181 | name : @textContent.trim() 182 | infoStr 183 | loc 184 | labName 185 | week : parseWeekStr(infoStr) 186 | beginT 187 | endT 188 | } 189 | ) 190 | {Gr, Gl} 191 | 192 | 193 | ############ 194 | ## combine 195 | getOrigin=(Gr, Gl, L)-> 196 | lastDay=L[L.length-1] 197 | lastItems=lastDay.items 198 | lastItem=lastItems[lastItems.length-1] 199 | z=lastDay.ymd.day() 200 | if z==0 then z=7 # moment.day() returns 0 for Sunday 201 | 202 | maxW=0 203 | for p in [6..1] by -1 204 | for it in Gr[z][p] 205 | if it.name==lastItem.name && (w=it.week[it.week.length-1])>maxW 206 | maxW=w 207 | for it in Gl[z][p] 208 | if it.name==lastItem.name && (w=it.week[it.week.length-1])>maxW 209 | maxW=w 210 | 211 | lastDay.ymd.clone().subtract(maxW-1, 'weeks').subtract(z-1, 'days') 212 | 213 | # remap L to Lrel[day-since-origin] 214 | getLrel=(L, origin)-> 215 | Lrel=[] 216 | for x in L 217 | Lrel[x.ymd.diff(origin, 'days')]=x.items 218 | Lrel 219 | 220 | # complete G using information from Lrel 221 | # time priority: G-side > L-side > default(neither G nor L displays time) 222 | combine=(G, Lrel, cat, origin)-> 223 | for z in [1..7] by 1 224 | for p in [1..6] by 1 225 | for gi in G[z][p] 226 | w=gi.week 227 | w=w[w.length-1] 228 | rel=(w-1)*7+(z-1) 229 | if (bin=Lrel[rel]) then for li in bin 230 | if !li.matched && li.cat==cat && li.name==gi.name 231 | li.matched=true 232 | gi.beginT||=li.beginT # G > L 233 | gi.endT ||=li.endT 234 | gi.loc=li.loc 235 | break 236 | gi.beginT||=period[p].beginT # L > default 237 | gi.endT ||=period[p].endT 238 | G 239 | 240 | 241 | ############ 242 | ## ical 243 | ICAL_HEADER=""" 244 | BEGIN:VCALENDAR 245 | PRODID:-//smilekzs//thucal//EN 246 | VERSION:2.0 247 | CALSCALE:GREGORIAN 248 | METHOD:PUBLISH 249 | X-WR-TIMEZONE:Asia/Shanghai 250 | BEGIN:VTIMEZONE 251 | TZID:Asia/Shanghai 252 | X-LIC-LOCATION:Asia/Shanghai 253 | BEGIN:STANDARD 254 | TZOFFSETFROM:+0800 255 | TZOFFSETTO:+0800 256 | TZNAME:CST 257 | DTSTART:19700101T000000 258 | END:STANDARD 259 | END:VTIMEZONE 260 | """ 261 | ICAL_FOOTER=""" 262 | END:VCALENDAR 263 | """ 264 | ICAL_EVENT=""" 265 | BEGIN:VEVENT 266 | SUMMARY: 267 | LOCATION: 268 | DESCRIPTION: 269 | DTSTART;TZID=Asia/Shanghai: 270 | DTEND;TZID=Asia/Shanghai: 271 | SEQUENCE:0 272 | UID: 273 | TRANSP:OPAQUE 274 | STATUS:CONFIRMED 275 | END:VEVENT 276 | """ 277 | ICAL_XRULE="RULE:FREQ=WEEKLY;COUNT=\n" 278 | ICAL_XDATE="DATE;TZID=Asia/Shanghai:\n" 279 | ICAL_WEEK=""" 280 | BEGIN:VEVENT 281 | SUMMARY: 282 | DTSTART;VALUE=DATE: 283 | DTEND;VALUE=DATE: 284 | SEQUENCE:0 285 | UID: 286 | TRANSP:TRANSPARENT 287 | STATUS:CONFIRMED 288 | END:VEVENT 289 | """ 290 | ical=new -> 291 | @uidSeq=0 292 | @getUid=->(@uidSeq++)+'.'+@uidBase 293 | @escape=(s)-> 294 | s.replace(/,/g, '\\,') 295 | @template=(tmpl, obj)-> 296 | ret=tmpl 297 | for k, v of obj 298 | ret=ret.replace(RegExp('<'+k+'>', 'g'), v) 299 | ret 300 | @dateStr=(m)->m.format('YYYYMMDD') 301 | @timeStr=(base, offset)-> 302 | base.clone().add(offset).format('YYYYMMDD[T]HHmmss') 303 | @nameStr=(gi)-> 304 | ret=gi.name 305 | if gi.labName 306 | ret+=' ['+gi.labName+']' 307 | ret 308 | @makeRecur=(oz, gi)-> 309 | ws=gi.week 310 | l=ws.length 311 | 312 | # no recurrence 313 | if l==1 then return '' 314 | 315 | # consecutive week range => use RRULE instead 316 | w0=ws[0] 317 | wl=ws[l-1] 318 | n=wl-w0+1 319 | if l==n then return @template ICAL_XRULE, {x: 'R', n} 320 | 321 | # else => use RDATE 322 | # NOTE: some calendar implementation needs first "recurrence" 323 | list=ws.map (i)=> 324 | @timeStr(oz.clone().add(i-1, 'weeks'), gi.beginT) 325 | @template ICAL_XDATE, {x: 'R', list: list.join(',')} 326 | 327 | @makeG=(G, origin)-> 328 | ret=[] 329 | for z in [1..7] by 1 330 | oz=origin.clone().add(z-1, 'days') 331 | bin=[] 332 | seq=0 333 | for p in [1..6] by 1 334 | for gi in G[z][p] 335 | ow=oz.clone().add(gi.week[0]-1, 'weeks') 336 | bin.push { 337 | seq : seq++ 338 | name : @escape @nameStr gi 339 | loc : @escape gi.loc 340 | desc : @escape gi.infoStr 341 | start : @timeStr(ow, gi.beginT) 342 | end : @timeStr(ow, gi.endT ) 343 | recur : @makeRecur(oz, gi) 344 | uid : @getUid() 345 | } 346 | # remove duplicate entries within a day 347 | ret=ret.concat bin.sort((a, b)-> 348 | cmp(a.name, b.name)||cmp(a.start, b.start) 349 | ).filter((x, i, a)-> 350 | xp=a[i-1] 351 | i==0 || x.name!=xp.name || x.start!=xp.start || x.end!=xp.end 352 | ).sort((a, b)-> 353 | a.seq-b.seq 354 | ) 355 | ret.map((x)=>@template ICAL_EVENT, x).join('\n') 356 | @makeW=(origin, nameFactory)-> 357 | (for w in [1..16] by 1 358 | start=origin.clone().add(w-1, 'weeks') 359 | end=start.clone().add(1, 'days') 360 | @template ICAL_WEEK, { 361 | name : @escape nameFactory w 362 | start : @dateStr start 363 | end : @dateStr end 364 | uid : @getUid() 365 | } 366 | ).join('\n') 367 | 368 | @make=(Gr, Gl, origin)-> 369 | @uidBase=moment().unix()+'@thucal' 370 | [ 371 | ICAL_HEADER 372 | @makeG(Gr, origin) 373 | @makeG(Gl, origin) 374 | @makeW(origin, (w)->"第#{w}周") 375 | ICAL_FOOTER 376 | ].join('\n') 377 | this 378 | 379 | 380 | ############ 381 | ## I/O 382 | 383 | ERR_MSG_LIST='list错误:检查是否已登录info' 384 | stringify=(p)-> 385 | (for k, v of p 386 | (encodeURIComponent(k) + '=' + encodeURIComponent(v)) 387 | ).join('&') 388 | get_L=(autocb)-> 389 | await GM_xmlhttpRequest { 390 | url: thucal.params.listUrl + '?m=' + thucal.params.listVerb 391 | method: 'GET' 392 | onload: defer(resp) 393 | onerror: (err)-> 394 | thucal.ui.log ERR_MSG_LIST 395 | throw Error('get_L: no token: ' + err.toString()) 396 | } 397 | if !(match=/name="token" value="([\da-f]+)"/.exec(resp.responseText))? 398 | thucal.ui.log ERR_MSG_LIST 399 | throw Error 'get_L: no token: response does not contain token' 400 | params= 401 | 'm': thucal.params.listVerb 402 | 'role': thucal.params.listRole 403 | 'grrlID': '' 404 | 'displayType': '' 405 | 'token': match[1] 406 | 'p_start_date': moment().format('YYYYMMDD') 407 | 'p_end_date': moment().add(1, 'years').format('YYYYMMDD') 408 | await GM_xmlhttpRequest { 409 | url: thucal.params.listUrl 410 | method: 'POST' 411 | headers: 412 | "Content-Type": "application/x-www-form-urlencoded" 413 | data: stringify(params) 414 | onload: defer(resp) 415 | onerror: (err)-> 416 | thucal.ui.log ERR_MSG_LIST 417 | throw Error('get_L: post error: ' + err.toString()) 418 | } 419 | $(resp.responseText).filter('a') 420 | download=(cont, name)-> 421 | b=new Blob([cont], {type: 'text/calendar'}) 422 | saveAs(b, name) 423 | 424 | 425 | ############ 426 | ## userscript logic 427 | unsafeWindow.thucal=thucal=new -> 428 | @lib={$, moment, saveAs} 429 | @init=-> 430 | 431 | ## ui 432 | 433 | @ui={} 434 | # button 435 | $('div.tddr').prepend(""" 436 | 437 | """) 438 | @ui.button=$('#thucal_button') 439 | @ui.button.on 'click', =>@make() 440 | # log 441 | $('#a1_1').parentsUntil('div.xinXi2').last().after(""" 442 |
447 | """) 448 | @ui.status=$('#thucal_status') 449 | @ui.log=(s)->@status.append(s+'\n') 450 | 451 | ## params 452 | 453 | if document.location.toString().match(/Yjs/) 454 | @params= 455 | listUrl: 'http://zhjw.cic.tsinghua.edu.cn/jxmh.do' 456 | listVerb: 'yjs_jxrl_all' 457 | listRole: 'yjs' 458 | else 459 | @params= 460 | listUrl: 'http://zhjw.cic.tsinghua.edu.cn/jxmh.do' 461 | listVerb: 'bks_jxrl_all' 462 | listRole: 'bks' 463 | 464 | @make=-> 465 | @ui.log "******THUCAL2******" 466 | termIdP=parseTermId($('input[name=p_xnxq]').val()) 467 | term=printTermId(termIdP) 468 | @ui.log '学期:'+term 469 | if termIdP.termN!=1 && termIdP.termN!=2 470 | @ui.log '不支持小学期!' 471 | return 472 | 473 | await get_L defer(Lraw) 474 | @ui.log 'list完成' 475 | 476 | try 477 | L=parse_L(Lraw, termIdP) 478 | {Gr, Gl}=parse_G($(document)) 479 | origin=getOrigin(Gr, Gl, L) 480 | Lrel=getLrel(L, origin) 481 | combine(Gr, Lrel, '上课', origin) 482 | combine(Gl, Lrel, '实验', origin) 483 | @ui.log '分析完成' 484 | catch e 485 | @ui.log '分析错误:'+e.toString() 486 | return console.error e 487 | 488 | try 489 | ret=ical.make(Gr, Gl, origin) 490 | #console.log ret 491 | download(ret, "thucal-#{term}.ics") 492 | @ui.log '导出成功!' 493 | catch e 494 | @ui.log '导出错误:'+e.toString() 495 | return console.error e 496 | this 497 | 498 | $(document).ready(->thucal.init()) 499 | --------------------------------------------------------------------------------