From 3ed5b161ecda04a994d3956c6094f7b72ef87c09 Mon Sep 17 00:00:00 2001 From: Bridger Maxwell Date: Sun, 25 Mar 2012 20:08:03 -0700 Subject: [PATCH 01/50] Moved simplewiki autosuggest js (it didn't get migrated). Fixed a minor typo in settings.py (it suggested the wrong override location for the static js files) --- .../media/css/autosuggest_inquisitor.css | 177 ----------- djangoapps/simplewiki/media/css/base.css | 281 ------------------ .../simplewiki/media/css/base_print.css | 6 - .../css/img_inquisitor/_source/as_pointer.png | Bin 27142 -> 0 bytes .../css/img_inquisitor/_source/li_corner.png | Bin 28442 -> 0 bytes .../css/img_inquisitor/_source/ul_corner.png | Bin 27649 -> 0 bytes .../media/css/img_inquisitor/as_pointer.gif | Bin 66 -> 0 bytes .../media/css/img_inquisitor/hl_corner_bl.gif | Bin 73 -> 0 bytes .../media/css/img_inquisitor/hl_corner_br.gif | Bin 73 -> 0 bytes .../media/css/img_inquisitor/hl_corner_tl.gif | Bin 73 -> 0 bytes .../media/css/img_inquisitor/hl_corner_tr.gif | Bin 73 -> 0 bytes .../media/css/img_inquisitor/ul_corner_bl.gif | Bin 49 -> 0 bytes .../media/css/img_inquisitor/ul_corner_br.gif | Bin 49 -> 0 bytes .../media/css/img_inquisitor/ul_corner_tl.gif | Bin 50 -> 0 bytes .../media/css/img_inquisitor/ul_corner_tr.gif | Bin 50 -> 0 bytes .../simplewiki/media/img/box_corner_bl.gif | Bin 49 -> 0 bytes .../simplewiki/media/img/box_corner_br.gif | Bin 49 -> 0 bytes .../simplewiki/media/img/box_corner_tl.gif | Bin 50 -> 0 bytes .../simplewiki/media/img/box_corner_tr.gif | Bin 50 -> 0 bytes djangoapps/simplewiki/media/img/delete.gif | Bin 130 -> 0 bytes .../simplewiki/media/img/delete_grey.gif | Bin 130 -> 0 bytes settings.py | 2 +- .../js/simplewiki}/bsn.AutoSuggest_c_2.0.js | 0 23 files changed, 1 insertion(+), 465 deletions(-) delete mode 100644 djangoapps/simplewiki/media/css/autosuggest_inquisitor.css delete mode 100644 djangoapps/simplewiki/media/css/base.css delete mode 100644 djangoapps/simplewiki/media/css/base_print.css delete mode 100644 djangoapps/simplewiki/media/css/img_inquisitor/_source/as_pointer.png delete mode 100644 djangoapps/simplewiki/media/css/img_inquisitor/_source/li_corner.png delete mode 100644 djangoapps/simplewiki/media/css/img_inquisitor/_source/ul_corner.png delete mode 100644 djangoapps/simplewiki/media/css/img_inquisitor/as_pointer.gif delete mode 100644 djangoapps/simplewiki/media/css/img_inquisitor/hl_corner_bl.gif delete mode 100644 djangoapps/simplewiki/media/css/img_inquisitor/hl_corner_br.gif delete mode 100644 djangoapps/simplewiki/media/css/img_inquisitor/hl_corner_tl.gif delete mode 100644 djangoapps/simplewiki/media/css/img_inquisitor/hl_corner_tr.gif delete mode 100644 djangoapps/simplewiki/media/css/img_inquisitor/ul_corner_bl.gif delete mode 100644 djangoapps/simplewiki/media/css/img_inquisitor/ul_corner_br.gif delete mode 100644 djangoapps/simplewiki/media/css/img_inquisitor/ul_corner_tl.gif delete mode 100644 djangoapps/simplewiki/media/css/img_inquisitor/ul_corner_tr.gif delete mode 100644 djangoapps/simplewiki/media/img/box_corner_bl.gif delete mode 100644 djangoapps/simplewiki/media/img/box_corner_br.gif delete mode 100644 djangoapps/simplewiki/media/img/box_corner_tl.gif delete mode 100644 djangoapps/simplewiki/media/img/box_corner_tr.gif delete mode 100644 djangoapps/simplewiki/media/img/delete.gif delete mode 100644 djangoapps/simplewiki/media/img/delete_grey.gif rename {djangoapps/simplewiki/media/js => static/js/simplewiki}/bsn.AutoSuggest_c_2.0.js (100%) diff --git a/djangoapps/simplewiki/media/css/autosuggest_inquisitor.css b/djangoapps/simplewiki/media/css/autosuggest_inquisitor.css deleted file mode 100644 index fc407f6f26..0000000000 --- a/djangoapps/simplewiki/media/css/autosuggest_inquisitor.css +++ /dev/null @@ -1,177 +0,0 @@ -/* -================================================ -autosuggest, inquisitor style -================================================ -*/ - -body -{ - position: relative; -} - - -div.autosuggest -{ - position: absolute; - background-image: url(img_inquisitor/as_pointer.gif); - background-position: top; - background-repeat: no-repeat; - padding: 10px 0 0 0; -} - -div.autosuggest div.as_header, -div.autosuggest div.as_footer -{ - position: relative; - height: 6px; - padding: 0 6px; - background-image: url(img_inquisitor/ul_corner_tr.gif); - background-position: top right; - background-repeat: no-repeat; - overflow: hidden; -} -div.autosuggest div.as_footer -{ - background-image: url(img_inquisitor/ul_corner_br.gif); -} - -div.autosuggest div.as_header div.as_corner, -div.autosuggest div.as_footer div.as_corner -{ - position: absolute; - top: 0; - left: 0; - height: 6px; - width: 6px; - background-image: url(img_inquisitor/ul_corner_tl.gif); - background-position: top left; - background-repeat: no-repeat; -} -div.autosuggest div.as_footer div.as_corner -{ - background-image: url(img_inquisitor/ul_corner_bl.gif); -} -div.autosuggest div.as_header div.as_bar, -div.autosuggest div.as_footer div.as_bar -{ - height: 6px; - overflow: hidden; - background-color: #333; -} - - -div.autosuggest ul -{ - list-style: none; - margin: 0 0 -4px 0; - padding: 0; - overflow: hidden; - background-color: #333; -} - -div.autosuggest ul li -{ - color: #ccc; - padding: 0; - margin: 0 4px 4px; - text-align: left; -} - -div.autosuggest ul li a -{ - color: #ccc; - display: block; - text-decoration: none; - background-color: transparent; - text-shadow: #000 0px 0px 5px; - position: relative; - padding: 0; - width: 100%; -} -div.autosuggest ul li a:hover -{ - background-color: #444; -} -div.autosuggest ul li.as_highlight a:hover -{ - background-color: #1B5CCD; -} - -div.autosuggest ul li a span -{ - display: block; - padding: 3px 6px; - font-weight: bold; -} - -div.autosuggest ul li a span small -{ - font-weight: normal; - color: #999; -} - -div.autosuggest ul li.as_highlight a span small -{ - color: #ccc; -} - -div.autosuggest ul li.as_highlight a -{ - color: #fff; - background-color: #1B5CCD; - background-image: url(img_inquisitor/hl_corner_br.gif); - background-position: bottom right; - background-repeat: no-repeat; -} - -div.autosuggest ul li.as_highlight a span -{ - background-image: url(img_inquisitor/hl_corner_bl.gif); - background-position: bottom left; - background-repeat: no-repeat; -} - -div.autosuggest ul li a .tl, -div.autosuggest ul li a .tr -{ - background-image: transparent; - background-repeat: no-repeat; - width: 6px; - height: 6px; - position: absolute; - top: 0; - padding: 0; - margin: 0; -} -div.autosuggest ul li a .tr -{ - right: 0; -} - -div.autosuggest ul li.as_highlight a .tl -{ - left: 0; - background-image: url(img_inquisitor/hl_corner_tl.gif); - background-position: bottom left; -} - -div.autosuggest ul li.as_highlight a .tr -{ - right: 0; - background-image: url(img_inquisitor/hl_corner_tr.gif); - background-position: bottom right; -} - - - -div.autosuggest ul li.as_warning -{ - font-weight: bold; - text-align: center; -} - -div.autosuggest ul em -{ - font-style: normal; - color: #6EADE7; -} \ No newline at end of file diff --git a/djangoapps/simplewiki/media/css/base.css b/djangoapps/simplewiki/media/css/base.css deleted file mode 100644 index 8c55d0c2f4..0000000000 --- a/djangoapps/simplewiki/media/css/base.css +++ /dev/null @@ -1,281 +0,0 @@ -body -{ - font-family: 'Lucida Sans', 'Sans'; -} - -a img -{ - border: 0; -} - -div#wiki_article a { - color: #06d; - text-decoration: none; -} - -div#wiki_article a:hover { - color: #f82; - text-decoration: underline; -} - -hr -{ - background-color: #def; - height: 2px; - border: 0; -} - -div#wiki_article .toc a -{ - color: #025 -} - -div#wiki_article p -{ - /* font-size: 90%; looks funny when combined with lists/tables */ - line-height: 140%; -} -div#wiki_article h1 -{ - font-size: 200%; - font-weight: normal; - color: #048; -} - -div#wiki_article h2 -{ - font-size: 150%; - font-weight: normal; - color: #025; -} - -div#wiki_article h3 -{ - font-size: 120%; - font-weight: bold; - color: #000; -} - -table -{ - border: 1px solid black; - border-collapse: collapse; - margin: 12px; -} - -table tr.dark -{ - background-color: #F3F3F3; -} - -table thead tr -{ - background-color: #def; - border-bottom: 2px solid black; -} - -table td, th -{ - padding: 6px 10px 6px 10px; - border: 1px solid black; -} - -table thead th -{ - padding-bottom: 8px; - padding-top: 8px; -} - -div#wiki_panel -{ - float: right; -} - -div.wiki_box -{ - width: 230px; - padding: 10px; - color: #fff; - font-size: 80%; -} - -div.wiki_box div.wiki_box_contents -{ background-color: #222; - padding: 5px 10px;} - -div.wiki_box div.wiki_box_header, -div.wiki_box div.wiki_box_footer -{ - position: relative; - height: 6px; - padding: 0 6px; - background-image: url(../img/box_corner_tr.gif); - background-position: top right; - background-repeat: no-repeat; - overflow: hidden; -} - -div.wiki_box div.wiki_box_footer -{ - background-image: url(../img/box_corner_br.gif); -} - -div.wiki_box div.wiki_box_header div.wiki_box_corner, -div.wiki_box div.wiki_box_footer div.wiki_box_corner -{ - position: absolute; - top: 0; - left: 0; - height: 6px; - width: 6px; - background-image: url(../img/box_corner_tl.gif); - background-position: top left; - background-repeat: no-repeat; -} - -div.wiki_box div.wiki_box_footer div.wiki_box_corner -{ - background-image: url(../img/box_corner_bl.gif); -} - -div.wiki_box div.wiki_box_header div.wiki_box_bar, -div.wiki_box div.wiki_box_footer div.wiki_box_bar -{ - height: 6px; - overflow: hidden; - background-color: #222; -} - - -div.wiki_box a -{ - color: #acf; -} - -div.wiki_box p -{ - margin: 5px 0; -} - -div.wiki_box ul -{ - padding-left: 20px; - margin-left: 0; -} - -div.wiki_box div.wiki_box_title -{ - margin-bottom: 5px; - font-size: 140%; -} - -form#wiki_revision #id_contents -{ - width:500px; - height: 400px; - font-family: monospace; -} - -form#wiki_revision #id_title -{ - width: 500px; -} - -form#wiki_revision #id_revision_text -{ - width: 500px; -} - -table#wiki_revision_table -{ - border: none; - border-collapse: collapse; - padding-right: 250px; -} - -table#wiki_revision_table th -{ - border: none; - text-align: left; - vertical-align: top; -} - -table#wiki_revision_table td -{ - border: none; -} - -table#wiki_history_table -{ - border-collapse: collapse; - border-spacing: 0; - padding-right: 250px; -} - -table#wiki_history_table th#modified -{ - width: 220px; -} - -table#wiki_history_table td -{ - border: none; -} - -table#wiki_history_table tbody tr -{ - border-bottom: 1px solid black; -} - -table#wiki_history_table tbody td -{ - vertical-align: top; - padding: 5px; -} - -table#wiki_history_table tfoot td -{ - border: none; -} - -table#wiki_history_table tbody td.diff -{ - font-family: monospace; - overflow: hidden; - border-left: 1px dotted black; - border-right: 1px dotted black; -} - -table#wiki_history_table th -{ - text-align: left; -} - -div#wiki_attach_progress_container -{ - background-color: #333; - width: 100%; - height: 20px; - display: none; -} - -div#wiki_attach_progress -{ - width: 25%; - background-color: #999; -} - -blockquote { - margin-top: 15px; - margin-bottom: 15px; - margin-left: 50px; - padding-left: 15px; - border-left: 3px solid #666; - color: #999; - max-width: 400px ; -} - -blockquote p { - margin-top: 8px; - margin-bottom: 8px; -} diff --git a/djangoapps/simplewiki/media/css/base_print.css b/djangoapps/simplewiki/media/css/base_print.css deleted file mode 100644 index 4c887c8aa8..0000000000 --- a/djangoapps/simplewiki/media/css/base_print.css +++ /dev/null @@ -1,6 +0,0 @@ -div#wiki_panel -{ - display:none; -} - - diff --git a/djangoapps/simplewiki/media/css/img_inquisitor/_source/as_pointer.png b/djangoapps/simplewiki/media/css/img_inquisitor/_source/as_pointer.png deleted file mode 100644 index a0d43aaf0e9d4b9700eac95499f5614ccbd91cd4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 27142 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

+|Jyh+BejmcwL=I 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 diff --git a/djangoapps/simplewiki/media/css/img_inquisitor/as_pointer.gif b/djangoapps/simplewiki/media/css/img_inquisitor/as_pointer.gif deleted file mode 100644 index dbc21220f6ff01769188756f12651a07d6b4c309..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 66 zcmZ?wbhEHb6k*_EXkcJ4Ha7nM|G(l-7DjdkMg|=QAOOiQFiG~*Gn~j>HK!%O$m{Ll T%Az+l_f`9%Ef-}lGFSruI~EhJ diff --git a/djangoapps/simplewiki/media/css/img_inquisitor/hl_corner_bl.gif b/djangoapps/simplewiki/media/css/img_inquisitor/hl_corner_bl.gif deleted file mode 100644 index b701d01c93cc7746369c52cba6c05e5d65946822..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 73 zcmZ?wbhEHbWMg1sSj4~}9dlO8DMuk>gORDt|Ns9PU_kLF3nM!N3xf_w0Hl_InUBLu T=k&vg4@D%dJQ9pyVz34PYIYFP diff --git a/djangoapps/simplewiki/media/css/img_inquisitor/hl_corner_br.gif b/djangoapps/simplewiki/media/css/img_inquisitor/hl_corner_br.gif deleted file mode 100644 index 11debd7fec2c4fe5442211abfead3fd107d2f4c8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 73 zcmZ?wbhEHbWMg1sSj4~}9dlO8DMuk>gORDt|Ns9PU_kLF3nM!N3xf_w0Hl_Ina_i3 T&5=VJ4?I|r6eIY>gTWdAc$pGy diff --git a/djangoapps/simplewiki/media/css/img_inquisitor/hl_corner_tl.gif b/djangoapps/simplewiki/media/css/img_inquisitor/hl_corner_tl.gif deleted file mode 100644 index 1c2bbaf7a7430c4a574a83f2ca27bd7df5770879..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 73 zcmZ?wbhEHbWMg1sSj4~}9dlO8DMuk>gQ1!2|Ns9PU_kLF3nM!N3xf_w0Hl_Ina@Is T>DGaR5=uKiOyVeEVz34PW|RgQ1!2|Ns9PU_kLF3nM!N3xf_w0Hl_InU6#0 T@`ppVrX95hg;u09F<1itYmX4% diff --git a/djangoapps/simplewiki/media/css/img_inquisitor/ul_corner_bl.gif b/djangoapps/simplewiki/media/css/img_inquisitor/ul_corner_bl.gif deleted file mode 100644 index 4fa8c8e11b7111278b74b750c6c1fb19cf00c0a4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 49 zcmZ?wbhEHbWMg1sXkcV8Ha2EpU{L(Y!pOkD$e;sc1I5`G7??O(`o(ritc}xVum%9L CT?d>1 diff --git a/djangoapps/simplewiki/media/css/img_inquisitor/ul_corner_br.gif b/djangoapps/simplewiki/media/css/img_inquisitor/ul_corner_br.gif deleted file mode 100644 index f589431b49668999d9394d1c1f0c30e4c8b3381d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 49 zcmZ?wbhEHbWMg1sXkcV8Ha2EpU{L(Y!pOkD$e;sc1I5`G7??O(`ZFiiJXFwOum%9P CLv0}%XHFqXYemi;cx9!_M&ztx2^5y^k|9_f1 zdFs@u3`junCkrD312=;XNDO2K1B-sa+Qf5)PI>`T!d(nRIUgxqqjev@<|JIi^PEuX0%AE`h)&Qu)FYy2X diff --git a/settings.py b/settings.py index f819019122..d4dbd4d79f 100644 --- a/settings.py +++ b/settings.py @@ -12,7 +12,7 @@ DEFAULT_GROUPS = [] STATIC_GRAB = False DEV_CONTENT = True -LIB_URL = '/static/lib/' +LIB_URL = '/static/js/' LIB_URL = 'https://mitxstatic.s3.amazonaws.com/js/' BOOK_URL = '/static/book/' BOOK_URL = 'https://mitxstatic.s3.amazonaws.com/book_images/' diff --git a/djangoapps/simplewiki/media/js/bsn.AutoSuggest_c_2.0.js b/static/js/simplewiki/bsn.AutoSuggest_c_2.0.js similarity index 100% rename from djangoapps/simplewiki/media/js/bsn.AutoSuggest_c_2.0.js rename to static/js/simplewiki/bsn.AutoSuggest_c_2.0.js From 8fce7066dafe6d89039703138ce957e38ccc98b1 Mon Sep 17 00:00:00 2001 From: Bridger Maxwell Date: Sun, 25 Mar 2012 20:39:21 -0700 Subject: [PATCH 02/50] You can now edit a circuit directly on the edit page of the wiki. These edits show up in the history. This hasn't been tested much. --- djangoapps/simplewiki/mdx_circuit.py | 11 +- static/js/CodeMirror/codemirror.css | 112 + static/js/CodeMirror/codemirror.js | 3015 +++++++++++++++++++++++++ static/js/CodeMirror/mitx_markdown.js | 345 +++ static/js/CodeMirror/xml.js | 267 +++ templates/simplewiki_base.html | 2 +- templates/simplewiki_edit.html | 56 +- 7 files changed, 3789 insertions(+), 19 deletions(-) create mode 100644 static/js/CodeMirror/codemirror.css create mode 100644 static/js/CodeMirror/codemirror.js create mode 100644 static/js/CodeMirror/mitx_markdown.js create mode 100644 static/js/CodeMirror/xml.js diff --git a/djangoapps/simplewiki/mdx_circuit.py b/djangoapps/simplewiki/mdx_circuit.py index 465e7d9a3a..33377eb019 100755 --- a/djangoapps/simplewiki/mdx_circuit.py +++ b/djangoapps/simplewiki/mdx_circuit.py @@ -30,15 +30,14 @@ class CircuitExtension(markdown.Extension): md.inlinePatterns.add(name, pattern, "[a-zA-Z0-9]*)$') + self.add_inline(md, 'circuit', CircuitLink, r'^circuit-schematic:(?P.*)$') class CircuitLink(markdown.inlinepatterns.Pattern): def handleMatch(self, m): - name = m.group('name') - if not name.isalnum(): - return etree.fromstring("

Circuit name must be alphanumeric
") - - return etree.fromstring(render_to_string('show_circuit.html', {'name':name})) + data = m.group('data') + + ##TODO: We need to html escape the data + return etree.fromstring("") def makeExtension(configs=None) : diff --git a/static/js/CodeMirror/codemirror.css b/static/js/CodeMirror/codemirror.css new file mode 100644 index 0000000000..2d79f4aa79 --- /dev/null +++ b/static/js/CodeMirror/codemirror.css @@ -0,0 +1,112 @@ +.CodeMirror { + line-height: 1em; + font-family: monospace; +} + +.CodeMirror-scroll { + overflow: auto; + height: 300px; + /* This is needed to prevent an IE[67] bug where the scrolled content + is visible outside of the scrolling box. */ + position: relative; + outline: none; +} + +.CodeMirror-gutter { + position: absolute; left: 0; top: 0; + z-index: 10; + background-color: #f7f7f7; + border-right: 1px solid #eee; + min-width: 2em; + height: 100%; +} +.CodeMirror-gutter-text { + color: #aaa; + text-align: right; + padding: .4em .2em .4em .4em; + white-space: pre !important; +} +.CodeMirror-lines { + padding: .4em; + white-space: pre; +} + +.CodeMirror pre { + -moz-border-radius: 0; + -webkit-border-radius: 0; + -o-border-radius: 0; + border-radius: 0; + border-width: 0; margin: 0; padding: 0; background: transparent; + font-family: inherit; + font-size: inherit; + padding: 0; margin: 0; + white-space: pre; + word-wrap: normal; +} + +.CodeMirror-wrap pre { + word-wrap: break-word; + white-space: pre-wrap; +} +.CodeMirror-wrap .CodeMirror-scroll { + overflow-x: hidden; +} + +.CodeMirror textarea { + outline: none !important; +} + +.CodeMirror pre.CodeMirror-cursor { + z-index: 10; + position: absolute; + visibility: hidden; + border-left: 1px solid black; + border-right:none; + width:0; +} +.CodeMirror pre.CodeMirror-cursor.CodeMirror-overwrite {} +.CodeMirror-focused pre.CodeMirror-cursor { + visibility: visible; +} + +div.CodeMirror-selected { background: #d9d9d9; } +.CodeMirror-focused div.CodeMirror-selected { background: #d7d4f0; } + +.CodeMirror-searching { + background: #ffa; + background: rgba(255, 255, 0, .4); +} + +/* Default theme */ + +.cm-s-default span.cm-keyword {color: #708;} +.cm-s-default span.cm-atom {color: #219;} +.cm-s-default span.cm-number {color: #164;} +.cm-s-default span.cm-def {color: #00f;} +.cm-s-default span.cm-variable {color: black;} +.cm-s-default span.cm-variable-2 {color: #05a;} +.cm-s-default span.cm-variable-3 {color: #085;} +.cm-s-default span.cm-property {color: black;} +.cm-s-default span.cm-operator {color: black;} +.cm-s-default span.cm-comment {color: #a50;} +.cm-s-default span.cm-string {color: #a11;} +.cm-s-default span.cm-string-2 {color: #f50;} +.cm-s-default span.cm-meta {color: #555;} +.cm-s-default span.cm-error {color: #f00;} +.cm-s-default span.cm-qualifier {color: #555;} +.cm-s-default span.cm-builtin {color: #30a;} +.cm-s-default span.cm-bracket {color: #cc7;} +.cm-s-default span.cm-tag {color: #170;} +.cm-s-default span.cm-attribute {color: #00c;} +.cm-s-default span.cm-header {color: #a0a;} +.cm-s-default span.cm-quote {color: #090;} +.cm-s-default span.cm-hr {color: #999;} +.cm-s-default span.cm-link {color: #00c;} + +span.cm-header, span.cm-strong {font-weight: bold;} +span.cm-em {font-style: italic;} +span.cm-emstrong {font-style: italic; font-weight: bold;} +span.cm-link {text-decoration: underline;} + +div.CodeMirror span.CodeMirror-matchingbracket {color: #0f0;} +div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;} diff --git a/static/js/CodeMirror/codemirror.js b/static/js/CodeMirror/codemirror.js new file mode 100644 index 0000000000..c7d3c86899 --- /dev/null +++ b/static/js/CodeMirror/codemirror.js @@ -0,0 +1,3015 @@ +// All functions that need access to the editor's state live inside +// the CodeMirror function. Below that, at the bottom of the file, +// some utilities are defined. + +// CodeMirror is the only global var we claim +var CodeMirror = (function() { + // This is the function that produces an editor instance. Its + // closure is used to store the editor state. + function CodeMirror(place, givenOptions) { + // Determine effective options based on given values and defaults. + var options = {}, defaults = CodeMirror.defaults; + for (var opt in defaults) + if (defaults.hasOwnProperty(opt)) + options[opt] = (givenOptions && givenOptions.hasOwnProperty(opt) ? givenOptions : defaults)[opt]; + + // The element in which the editor lives. + var wrapper = document.createElement("div"); + wrapper.className = "CodeMirror" + (options.lineWrapping ? " CodeMirror-wrap" : ""); + // This mess creates the base DOM structure for the editor. + wrapper.innerHTML = + '
' + // Wraps and hides input textarea + '
' + + '
' + + '
' + // Set to the height of the text, causes scrolling + '
' + // Moved around its parent to cover visible view + '
' + + // Provides positioning relative to (visible) text origin + '
' + + '
' + + '
 
' + // Absolutely positioned blinky cursor + '
' + // DIVs containing the selection and the actual code + '
'; + if (place.appendChild) place.appendChild(wrapper); else place(wrapper); + // I've never seen more elegant code in my life. + var inputDiv = wrapper.firstChild, input = inputDiv.firstChild, + scroller = wrapper.lastChild, code = scroller.firstChild, + mover = code.firstChild, gutter = mover.firstChild, gutterText = gutter.firstChild, + lineSpace = gutter.nextSibling.firstChild, measure = lineSpace.firstChild, + cursor = measure.nextSibling, selectionDiv = cursor.nextSibling, + lineDiv = selectionDiv.nextSibling; + themeChanged(); + // Needed to hide big blue blinking cursor on Mobile Safari + if (ios) input.style.width = "0px"; + if (!webkit) lineSpace.draggable = true; + lineSpace.style.outline = "none"; + if (options.tabindex != null) input.tabIndex = options.tabindex; + if (options.autofocus) focusInput(); + if (!options.gutter && !options.lineNumbers) gutter.style.display = "none"; + // Needed to handle Tab key in KHTML + if (khtml) inputDiv.style.height = "1px", inputDiv.style.position = "absolute"; + + // Check for problem with IE innerHTML not working when we have a + // P (or similar) parent node. + try { stringWidth("x"); } + catch (e) { + if (e.message.match(/runtime/i)) + e = new Error("A CodeMirror inside a P-style element does not work in Internet Explorer. (innerHTML bug)"); + throw e; + } + + // Delayed object wrap timeouts, making sure only one is active. blinker holds an interval. + var poll = new Delayed(), highlight = new Delayed(), blinker; + + // mode holds a mode API object. doc is the tree of Line objects, + // work an array of lines that should be parsed, and history the + // undo history (instance of History constructor). + var mode, doc = new BranchChunk([new LeafChunk([new Line("")])]), work, focused; + loadMode(); + // The selection. These are always maintained to point at valid + // positions. Inverted is used to remember that the user is + // selecting bottom-to-top. + var sel = {from: {line: 0, ch: 0}, to: {line: 0, ch: 0}, inverted: false}; + // Selection-related flags. shiftSelecting obviously tracks + // whether the user is holding shift. + var shiftSelecting, lastClick, lastDoubleClick, lastScrollPos = 0, draggingText, + overwrite = false, suppressEdits = false; + // Variables used by startOperation/endOperation to track what + // happened during the operation. + var updateInput, userSelChange, changes, textChanged, selectionChanged, leaveInputAlone, + gutterDirty, callbacks; + // Current visible range (may be bigger than the view window). + var displayOffset = 0, showingFrom = 0, showingTo = 0, lastSizeC = 0; + // bracketHighlighted is used to remember that a bracket has been + // marked. + var bracketHighlighted; + // Tracks the maximum line length so that the horizontal scrollbar + // can be kept static when scrolling. + var maxLine = "", maxWidth; + var tabCache = {}; + + // Initialize the content. + operation(function(){setValue(options.value || ""); updateInput = false;})(); + var history = new History(); + + // Register our event handlers. + connect(scroller, "mousedown", operation(onMouseDown)); + connect(scroller, "dblclick", operation(onDoubleClick)); + connect(lineSpace, "dragstart", onDragStart); + connect(lineSpace, "selectstart", e_preventDefault); + // Gecko browsers fire contextmenu *after* opening the menu, at + // which point we can't mess with it anymore. Context menu is + // handled in onMouseDown for Gecko. + if (!gecko) connect(scroller, "contextmenu", onContextMenu); + connect(scroller, "scroll", function() { + lastScrollPos = scroller.scrollTop; + updateDisplay([]); + if (options.fixedGutter) gutter.style.left = scroller.scrollLeft + "px"; + if (options.onScroll) options.onScroll(instance); + }); + connect(window, "resize", function() {updateDisplay(true);}); + connect(input, "keyup", operation(onKeyUp)); + connect(input, "input", fastPoll); + connect(input, "keydown", operation(onKeyDown)); + connect(input, "keypress", operation(onKeyPress)); + connect(input, "focus", onFocus); + connect(input, "blur", onBlur); + + connect(scroller, "dragenter", e_stop); + connect(scroller, "dragover", e_stop); + connect(scroller, "drop", operation(onDrop)); + connect(scroller, "paste", function(){focusInput(); fastPoll();}); + connect(input, "paste", fastPoll); + connect(input, "cut", operation(function(){ + if (!options.readOnly) replaceSelection(""); + })); + + // Needed to handle Tab key in KHTML + if (khtml) connect(code, "mouseup", function() { + if (document.activeElement == input) input.blur(); + focusInput(); + }); + + // IE throws unspecified error in certain cases, when + // trying to access activeElement before onload + var hasFocus; try { hasFocus = (document.activeElement == input); } catch(e) { } + if (hasFocus || options.autofocus) setTimeout(onFocus, 20); + else onBlur(); + + function isLine(l) {return l >= 0 && l < doc.size;} + // The instance object that we'll return. Mostly calls out to + // local functions in the CodeMirror function. Some do some extra + // range checking and/or clipping. operation is used to wrap the + // call so that changes it makes are tracked, and the display is + // updated afterwards. + var instance = wrapper.CodeMirror = { + getValue: getValue, + setValue: operation(setValue), + getSelection: getSelection, + replaceSelection: operation(replaceSelection), + focus: function(){window.focus(); focusInput(); onFocus(); fastPoll();}, + setOption: function(option, value) { + var oldVal = options[option]; + options[option] = value; + if (option == "mode" || option == "indentUnit") loadMode(); + else if (option == "readOnly" && value == "nocursor") {onBlur(); input.blur();} + else if (option == "readOnly" && !value) {resetInput(true);} + else if (option == "theme") themeChanged(); + else if (option == "lineWrapping" && oldVal != value) operation(wrappingChanged)(); + else if (option == "tabSize") updateDisplay(true); + if (option == "lineNumbers" || option == "gutter" || option == "firstLineNumber" || option == "theme") { + gutterChanged(); + updateDisplay(true); + } + }, + getOption: function(option) {return options[option];}, + undo: operation(undo), + redo: operation(redo), + indentLine: operation(function(n, dir) { + if (typeof dir != "string") { + if (dir == null) dir = options.smartIndent ? "smart" : "prev"; + else dir = dir ? "add" : "subtract"; + } + if (isLine(n)) indentLine(n, dir); + }), + indentSelection: operation(indentSelected), + historySize: function() {return {undo: history.done.length, redo: history.undone.length};}, + clearHistory: function() {history = new History();}, + matchBrackets: operation(function(){matchBrackets(true);}), + getTokenAt: operation(function(pos) { + pos = clipPos(pos); + return getLine(pos.line).getTokenAt(mode, getStateBefore(pos.line), pos.ch); + }), + getStateAfter: function(line) { + line = clipLine(line == null ? doc.size - 1: line); + return getStateBefore(line + 1); + }, + cursorCoords: function(start, mode) { + if (start == null) start = sel.inverted; + return this.charCoords(start ? sel.from : sel.to, mode); + }, + charCoords: function(pos, mode) { + pos = clipPos(pos); + if (mode == "local") return localCoords(pos, false); + if (mode == "div") return localCoords(pos, true); + return pageCoords(pos); + }, + coordsChar: function(coords) { + var off = eltOffset(lineSpace); + return coordsChar(coords.x - off.left, coords.y - off.top); + }, + markText: operation(markText), + setBookmark: setBookmark, + findMarksAt: findMarksAt, + setMarker: operation(addGutterMarker), + clearMarker: operation(removeGutterMarker), + setLineClass: operation(setLineClass), + hideLine: operation(function(h) {return setLineHidden(h, true);}), + showLine: operation(function(h) {return setLineHidden(h, false);}), + onDeleteLine: function(line, f) { + if (typeof line == "number") { + if (!isLine(line)) return null; + line = getLine(line); + } + (line.handlers || (line.handlers = [])).push(f); + return line; + }, + lineInfo: lineInfo, + addWidget: function(pos, node, scroll, vert, horiz) { + pos = localCoords(clipPos(pos)); + var top = pos.yBot, left = pos.x; + node.style.position = "absolute"; + code.appendChild(node); + if (vert == "over") top = pos.y; + else if (vert == "near") { + var vspace = Math.max(scroller.offsetHeight, doc.height * textHeight()), + hspace = Math.max(code.clientWidth, lineSpace.clientWidth) - paddingLeft(); + if (pos.yBot + node.offsetHeight > vspace && pos.y > node.offsetHeight) + top = pos.y - node.offsetHeight; + if (left + node.offsetWidth > hspace) + left = hspace - node.offsetWidth; + } + node.style.top = (top + paddingTop()) + "px"; + node.style.left = node.style.right = ""; + if (horiz == "right") { + left = code.clientWidth - node.offsetWidth; + node.style.right = "0px"; + } else { + if (horiz == "left") left = 0; + else if (horiz == "middle") left = (code.clientWidth - node.offsetWidth) / 2; + node.style.left = (left + paddingLeft()) + "px"; + } + if (scroll) + scrollIntoView(left, top, left + node.offsetWidth, top + node.offsetHeight); + }, + + lineCount: function() {return doc.size;}, + clipPos: clipPos, + getCursor: function(start) { + if (start == null) start = sel.inverted; + return copyPos(start ? sel.from : sel.to); + }, + somethingSelected: function() {return !posEq(sel.from, sel.to);}, + setCursor: operation(function(line, ch, user) { + if (ch == null && typeof line.line == "number") setCursor(line.line, line.ch, user); + else setCursor(line, ch, user); + }), + setSelection: operation(function(from, to, user) { + (user ? setSelectionUser : setSelection)(clipPos(from), clipPos(to || from)); + }), + getLine: function(line) {if (isLine(line)) return getLine(line).text;}, + getLineHandle: function(line) {if (isLine(line)) return getLine(line);}, + setLine: operation(function(line, text) { + if (isLine(line)) replaceRange(text, {line: line, ch: 0}, {line: line, ch: getLine(line).text.length}); + }), + removeLine: operation(function(line) { + if (isLine(line)) replaceRange("", {line: line, ch: 0}, clipPos({line: line+1, ch: 0})); + }), + replaceRange: operation(replaceRange), + getRange: function(from, to) {return getRange(clipPos(from), clipPos(to));}, + + triggerOnKeyDown: operation(onKeyDown), + execCommand: function(cmd) {return commands[cmd](instance);}, + // Stuff used by commands, probably not much use to outside code. + moveH: operation(moveH), + deleteH: operation(deleteH), + moveV: operation(moveV), + toggleOverwrite: function() { + if(overwrite){ + overwrite = false; + cursor.className = cursor.className.replace(" CodeMirror-overwrite", ""); + } else { + overwrite = true; + cursor.className += " CodeMirror-overwrite"; + } + }, + + posFromIndex: function(off) { + var lineNo = 0, ch; + doc.iter(0, doc.size, function(line) { + var sz = line.text.length + 1; + if (sz > off) { ch = off; return true; } + off -= sz; + ++lineNo; + }); + return clipPos({line: lineNo, ch: ch}); + }, + indexFromPos: function (coords) { + if (coords.line < 0 || coords.ch < 0) return 0; + var index = coords.ch; + doc.iter(0, coords.line, function (line) { + index += line.text.length + 1; + }); + return index; + }, + scrollTo: function(x, y) { + if (x != null) scroller.scrollLeft = x; + if (y != null) scroller.scrollTop = y; + updateDisplay([]); + }, + + operation: function(f){return operation(f)();}, + refresh: function(){ + updateDisplay(true); + if (scroller.scrollHeight > lastScrollPos) + scroller.scrollTop = lastScrollPos; + }, + getInputField: function(){return input;}, + getWrapperElement: function(){return wrapper;}, + getScrollerElement: function(){return scroller;}, + getGutterElement: function(){return gutter;} + }; + + function getLine(n) { return getLineAt(doc, n); } + function updateLineHeight(line, height) { + gutterDirty = true; + var diff = height - line.height; + for (var n = line; n; n = n.parent) n.height += diff; + } + + function setValue(code) { + var top = {line: 0, ch: 0}; + updateLines(top, {line: doc.size - 1, ch: getLine(doc.size-1).text.length}, + splitLines(code), top, top); + updateInput = true; + } + function getValue(code) { + var text = []; + doc.iter(0, doc.size, function(line) { text.push(line.text); }); + return text.join("\n"); + } + + function onMouseDown(e) { + setShift(e_prop(e, "shiftKey")); + // Check whether this is a click in a widget + for (var n = e_target(e); n != wrapper; n = n.parentNode) + if (n.parentNode == code && n != mover) return; + + // See if this is a click in the gutter + for (var n = e_target(e); n != wrapper; n = n.parentNode) + if (n.parentNode == gutterText) { + if (options.onGutterClick) + options.onGutterClick(instance, indexOf(gutterText.childNodes, n) + showingFrom, e); + return e_preventDefault(e); + } + + var start = posFromMouse(e); + + switch (e_button(e)) { + case 3: + if (gecko && !mac) onContextMenu(e); + return; + case 2: + if (start) setCursor(start.line, start.ch, true); + return; + } + // For button 1, if it was clicked inside the editor + // (posFromMouse returning non-null), we have to adjust the + // selection. + if (!start) {if (e_target(e) == scroller) e_preventDefault(e); return;} + + if (!focused) onFocus(); + + var now = +new Date; + if (lastDoubleClick && lastDoubleClick.time > now - 400 && posEq(lastDoubleClick.pos, start)) { + e_preventDefault(e); + setTimeout(focusInput, 20); + return selectLine(start.line); + } else if (lastClick && lastClick.time > now - 400 && posEq(lastClick.pos, start)) { + lastDoubleClick = {time: now, pos: start}; + e_preventDefault(e); + return selectWordAt(start); + } else { lastClick = {time: now, pos: start}; } + + var last = start, going; + if (dragAndDrop && !options.readOnly && !posEq(sel.from, sel.to) && + !posLess(start, sel.from) && !posLess(sel.to, start)) { + // Let the drag handler handle this. + if (webkit) lineSpace.draggable = true; + var up = connect(document, "mouseup", operation(function(e2) { + if (webkit) lineSpace.draggable = false; + draggingText = false; + up(); + if (Math.abs(e.clientX - e2.clientX) + Math.abs(e.clientY - e2.clientY) < 10) { + e_preventDefault(e2); + setCursor(start.line, start.ch, true); + focusInput(); + } + }), true); + draggingText = true; + // IE's approach to draggable + if (lineSpace.dragDrop) lineSpace.dragDrop(); + return; + } + e_preventDefault(e); + setCursor(start.line, start.ch, true); + + function extend(e) { + var cur = posFromMouse(e, true); + if (cur && !posEq(cur, last)) { + if (!focused) onFocus(); + last = cur; + setSelectionUser(start, cur); + updateInput = false; + var visible = visibleLines(); + if (cur.line >= visible.to || cur.line < visible.from) + going = setTimeout(operation(function(){extend(e);}), 150); + } + } + + function done(e) { + clearTimeout(going); + var cur = posFromMouse(e); + if (cur) setSelectionUser(start, cur); + e_preventDefault(e); + focusInput(); + updateInput = true; + move(); up(); + } + var move = connect(document, "mousemove", operation(function(e) { + clearTimeout(going); + e_preventDefault(e); + if (!ie && !e_button(e)) done(e); + else extend(e); + }), true); + var up = connect(document, "mouseup", operation(done), true); + } + function onDoubleClick(e) { + for (var n = e_target(e); n != wrapper; n = n.parentNode) + if (n.parentNode == gutterText) return e_preventDefault(e); + var start = posFromMouse(e); + if (!start) return; + lastDoubleClick = {time: +new Date, pos: start}; + e_preventDefault(e); + selectWordAt(start); + } + function onDrop(e) { + e.preventDefault(); + var pos = posFromMouse(e, true), files = e.dataTransfer.files; + if (!pos || options.readOnly) return; + if (files && files.length && window.FileReader && window.File) { + function loadFile(file, i) { + var reader = new FileReader; + reader.onload = function() { + text[i] = reader.result; + if (++read == n) { + pos = clipPos(pos); + operation(function() { + var end = replaceRange(text.join(""), pos, pos); + setSelectionUser(pos, end); + })(); + } + }; + reader.readAsText(file); + } + var n = files.length, text = Array(n), read = 0; + for (var i = 0; i < n; ++i) loadFile(files[i], i); + } + else { + try { + var text = e.dataTransfer.getData("Text"); + if (text) { + var curFrom = sel.from, curTo = sel.to; + setSelectionUser(pos, pos); + if (draggingText) replaceRange("", curFrom, curTo); + replaceSelection(text); + focusInput(); + } + } + catch(e){} + } + } + function onDragStart(e) { + var txt = getSelection(); + e.dataTransfer.setData("Text", txt); + + // Use dummy image instead of default browsers image. + if (gecko || chrome) { + var img = document.createElement('img'); + img.scr = 'data:image/gif;base64,R0lGODdhAgACAIAAAAAAAP///ywAAAAAAgACAAACAoRRADs='; //1x1 image + e.dataTransfer.setDragImage(img, 0, 0); + } + } + + function doHandleBinding(bound, dropShift) { + if (typeof bound == "string") { + bound = commands[bound]; + if (!bound) return false; + } + var prevShift = shiftSelecting; + try { + if (options.readOnly) suppressEdits = true; + if (dropShift) shiftSelecting = null; + bound(instance); + } catch(e) { + if (e != Pass) throw e; + return false; + } finally { + shiftSelecting = prevShift; + suppressEdits = false; + } + return true; + } + function handleKeyBinding(e) { + // Handle auto keymap transitions + var startMap = getKeyMap(options.keyMap), next = startMap.auto; + clearTimeout(maybeTransition); + if (next && !isModifierKey(e)) maybeTransition = setTimeout(function() { + if (getKeyMap(options.keyMap) == startMap) { + options.keyMap = (next.call ? next.call(null, instance) : next); + } + }, 50); + + var name = keyNames[e_prop(e, "keyCode")], handled = false; + if (name == null || e.altGraphKey) return false; + if (e_prop(e, "altKey")) name = "Alt-" + name; + if (e_prop(e, "ctrlKey")) name = "Ctrl-" + name; + if (e_prop(e, "metaKey")) name = "Cmd-" + name; + + if (e_prop(e, "shiftKey")) { + handled = lookupKey("Shift-" + name, options.extraKeys, options.keyMap, + function(b) {return doHandleBinding(b, true);}) + || lookupKey(name, options.extraKeys, options.keyMap, function(b) { + if (typeof b == "string" && /^go[A-Z]/.test(b)) return doHandleBinding(b); + }); + } else { + handled = lookupKey(name, options.extraKeys, options.keyMap, doHandleBinding); + } + if (handled) { + e_preventDefault(e); + if (ie) { e.oldKeyCode = e.keyCode; e.keyCode = 0; } + } + return handled; + } + function handleCharBinding(e, ch) { + var handled = lookupKey("'" + ch + "'", options.extraKeys, + options.keyMap, doHandleBinding); + if (handled) e_preventDefault(e); + return handled; + } + + var lastStoppedKey = null, maybeTransition; + function onKeyDown(e) { + if (!focused) onFocus(); + if (ie && e.keyCode == 27) { e.returnValue = false; } + if (pollingFast) { if (readInput()) pollingFast = false; } + if (options.onKeyEvent && options.onKeyEvent(instance, addStop(e))) return; + var code = e_prop(e, "keyCode"); + // IE does strange things with escape. + setShift(code == 16 || e_prop(e, "shiftKey")); + // First give onKeyEvent option a chance to handle this. + var handled = handleKeyBinding(e); + if (window.opera) { + lastStoppedKey = handled ? code : null; + // Opera has no cut event... we try to at least catch the key combo + if (!handled && code == 88 && e_prop(e, mac ? "metaKey" : "ctrlKey")) + replaceSelection(""); + } + } + function onKeyPress(e) { + if (pollingFast) readInput(); + if (options.onKeyEvent && options.onKeyEvent(instance, addStop(e))) return; + var keyCode = e_prop(e, "keyCode"), charCode = e_prop(e, "charCode"); + if (window.opera && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return;} + if (((window.opera && !e.which) || khtml) && handleKeyBinding(e)) return; + var ch = String.fromCharCode(charCode == null ? keyCode : charCode); + if (options.electricChars && mode.electricChars && options.smartIndent && !options.readOnly) { + if (mode.electricChars.indexOf(ch) > -1) + setTimeout(operation(function() {indentLine(sel.to.line, "smart");}), 75); + } + if (handleCharBinding(e, ch)) return; + fastPoll(); + } + function onKeyUp(e) { + if (options.onKeyEvent && options.onKeyEvent(instance, addStop(e))) return; + if (e_prop(e, "keyCode") == 16) shiftSelecting = null; + } + + function onFocus() { + if (options.readOnly == "nocursor") return; + if (!focused) { + if (options.onFocus) options.onFocus(instance); + focused = true; + if (wrapper.className.search(/\bCodeMirror-focused\b/) == -1) + wrapper.className += " CodeMirror-focused"; + if (!leaveInputAlone) resetInput(true); + } + slowPoll(); + restartBlink(); + } + function onBlur() { + if (focused) { + if (options.onBlur) options.onBlur(instance); + focused = false; + if (bracketHighlighted) + operation(function(){ + if (bracketHighlighted) { bracketHighlighted(); bracketHighlighted = null; } + })(); + wrapper.className = wrapper.className.replace(" CodeMirror-focused", ""); + } + clearInterval(blinker); + setTimeout(function() {if (!focused) shiftSelecting = null;}, 150); + } + + // Replace the range from from to to by the strings in newText. + // Afterwards, set the selection to selFrom, selTo. + function updateLines(from, to, newText, selFrom, selTo) { + if (suppressEdits) return; + if (history) { + var old = []; + doc.iter(from.line, to.line + 1, function(line) { old.push(line.text); }); + history.addChange(from.line, newText.length, old); + while (history.done.length > options.undoDepth) history.done.shift(); + } + updateLinesNoUndo(from, to, newText, selFrom, selTo); + } + function unredoHelper(from, to) { + if (!from.length) return; + var set = from.pop(), out = []; + for (var i = set.length - 1; i >= 0; i -= 1) { + var change = set[i]; + var replaced = [], end = change.start + change.added; + doc.iter(change.start, end, function(line) { replaced.push(line.text); }); + out.push({start: change.start, added: change.old.length, old: replaced}); + var pos = clipPos({line: change.start + change.old.length - 1, + ch: editEnd(replaced[replaced.length-1], change.old[change.old.length-1])}); + updateLinesNoUndo({line: change.start, ch: 0}, {line: end - 1, ch: getLine(end-1).text.length}, change.old, pos, pos); + } + updateInput = true; + to.push(out); + } + function undo() {unredoHelper(history.done, history.undone);} + function redo() {unredoHelper(history.undone, history.done);} + + function updateLinesNoUndo(from, to, newText, selFrom, selTo) { + if (suppressEdits) return; + var recomputeMaxLength = false, maxLineLength = maxLine.length; + if (!options.lineWrapping) + doc.iter(from.line, to.line, function(line) { + if (line.text.length == maxLineLength) {recomputeMaxLength = true; return true;} + }); + if (from.line != to.line || newText.length > 1) gutterDirty = true; + + var nlines = to.line - from.line, firstLine = getLine(from.line), lastLine = getLine(to.line); + // First adjust the line structure, taking some care to leave highlighting intact. + if (from.ch == 0 && to.ch == 0 && newText[newText.length - 1] == "") { + // This is a whole-line replace. Treated specially to make + // sure line objects move the way they are supposed to. + var added = [], prevLine = null; + if (from.line) { + prevLine = getLine(from.line - 1); + prevLine.fixMarkEnds(lastLine); + } else lastLine.fixMarkStarts(); + for (var i = 0, e = newText.length - 1; i < e; ++i) + added.push(Line.inheritMarks(newText[i], prevLine)); + if (nlines) doc.remove(from.line, nlines, callbacks); + if (added.length) doc.insert(from.line, added); + } else if (firstLine == lastLine) { + if (newText.length == 1) + firstLine.replace(from.ch, to.ch, newText[0]); + else { + lastLine = firstLine.split(to.ch, newText[newText.length-1]); + firstLine.replace(from.ch, null, newText[0]); + firstLine.fixMarkEnds(lastLine); + var added = []; + for (var i = 1, e = newText.length - 1; i < e; ++i) + added.push(Line.inheritMarks(newText[i], firstLine)); + added.push(lastLine); + doc.insert(from.line + 1, added); + } + } else if (newText.length == 1) { + firstLine.replace(from.ch, null, newText[0]); + lastLine.replace(null, to.ch, ""); + firstLine.append(lastLine); + doc.remove(from.line + 1, nlines, callbacks); + } else { + var added = []; + firstLine.replace(from.ch, null, newText[0]); + lastLine.replace(null, to.ch, newText[newText.length-1]); + firstLine.fixMarkEnds(lastLine); + for (var i = 1, e = newText.length - 1; i < e; ++i) + added.push(Line.inheritMarks(newText[i], firstLine)); + if (nlines > 1) doc.remove(from.line + 1, nlines - 1, callbacks); + doc.insert(from.line + 1, added); + } + if (options.lineWrapping) { + var perLine = scroller.clientWidth / charWidth() - 3; + doc.iter(from.line, from.line + newText.length, function(line) { + if (line.hidden) return; + var guess = Math.ceil(line.text.length / perLine) || 1; + if (guess != line.height) updateLineHeight(line, guess); + }); + } else { + doc.iter(from.line, i + newText.length, function(line) { + var l = line.text; + if (l.length > maxLineLength) { + maxLine = l; maxLineLength = l.length; maxWidth = null; + recomputeMaxLength = false; + } + }); + if (recomputeMaxLength) { + maxLineLength = 0; maxLine = ""; maxWidth = null; + doc.iter(0, doc.size, function(line) { + var l = line.text; + if (l.length > maxLineLength) { + maxLineLength = l.length; maxLine = l; + } + }); + } + } + + // Add these lines to the work array, so that they will be + // highlighted. Adjust work lines if lines were added/removed. + var newWork = [], lendiff = newText.length - nlines - 1; + for (var i = 0, l = work.length; i < l; ++i) { + var task = work[i]; + if (task < from.line) newWork.push(task); + else if (task > to.line) newWork.push(task + lendiff); + } + var hlEnd = from.line + Math.min(newText.length, 500); + highlightLines(from.line, hlEnd); + newWork.push(hlEnd); + work = newWork; + startWorker(100); + // Remember that these lines changed, for updating the display + changes.push({from: from.line, to: to.line + 1, diff: lendiff}); + var changeObj = {from: from, to: to, text: newText}; + if (textChanged) { + for (var cur = textChanged; cur.next; cur = cur.next) {} + cur.next = changeObj; + } else textChanged = changeObj; + + // Update the selection + function updateLine(n) {return n <= Math.min(to.line, to.line + lendiff) ? n : n + lendiff;} + setSelection(selFrom, selTo, updateLine(sel.from.line), updateLine(sel.to.line)); + + // Make sure the scroll-size div has the correct height. + if (scroller.clientHeight) + code.style.height = (doc.height * textHeight() + 2 * paddingTop()) + "px"; + } + + function replaceRange(code, from, to) { + from = clipPos(from); + if (!to) to = from; else to = clipPos(to); + code = splitLines(code); + function adjustPos(pos) { + if (posLess(pos, from)) return pos; + if (!posLess(to, pos)) return end; + var line = pos.line + code.length - (to.line - from.line) - 1; + var ch = pos.ch; + if (pos.line == to.line) + ch += code[code.length-1].length - (to.ch - (to.line == from.line ? from.ch : 0)); + return {line: line, ch: ch}; + } + var end; + replaceRange1(code, from, to, function(end1) { + end = end1; + return {from: adjustPos(sel.from), to: adjustPos(sel.to)}; + }); + return end; + } + function replaceSelection(code, collapse) { + var reposition = false; + if (code.length > 0) { + var fromLine = getLine(sel.from.line), toLine = getLine(sel.to.line); + if (fromLine.isWidgetBlock && sel.from.ch == fromLine.text.length) { + code = "\n" + code; + } else if (toLine.isWidgetBlock && sel.to.ch == 0) { + code = code + "\n"; + reposition = true; + } + } + + replaceRange1(splitLines(code), sel.from, sel.to, function(end) { + if (collapse == "end") return {from: end, to: end}; + else if (collapse == "start") return {from: sel.from, to: sel.from}; + else return {from: sel.from, to: end}; + }); + if (reposition) moveH(-1, "char"); + } + function replaceRange1(code, from, to, computeSel) { + var endch = code.length == 1 ? code[0].length + from.ch : code[code.length-1].length; + var newSel = computeSel({line: from.line + code.length - 1, ch: endch}); + updateLines(from, to, code, newSel.from, newSel.to); + } + + function getRange(from, to) { + var l1 = from.line, l2 = to.line; + if (l1 == l2) return getLine(l1).text.slice(from.ch, to.ch); + var code = [getLine(l1).text.slice(from.ch)]; + doc.iter(l1 + 1, l2, function(line) { code.push(line.text); }); + code.push(getLine(l2).text.slice(0, to.ch)); + return code.join("\n"); + } + function getSelection() { + return getRange(sel.from, sel.to); + } + + var pollingFast = false; // Ensures slowPoll doesn't cancel fastPoll + function slowPoll() { + if (pollingFast) return; + poll.set(options.pollInterval, function() { + startOperation(); + readInput(); + if (focused) slowPoll(); + endOperation(); + }); + } + function fastPoll() { + var missed = false; + pollingFast = true; + function p() { + startOperation(); + var changed = readInput(); + if (!changed && !missed) {missed = true; poll.set(60, p);} + else {pollingFast = false; slowPoll();} + endOperation(); + } + poll.set(20, p); + } + + // Previnput is a hack to work with IME. If we reset the textarea + // on every change, that breaks IME. So we look for changes + // compared to the previous content instead. (Modern browsers have + // events that indicate IME taking place, but these are not widely + // supported or compatible enough yet to rely on.) + var prevInput = ""; + function readInput() { + if (leaveInputAlone || !focused || hasSelection(input) || options.readOnly) return false; + var text = input.value; + if (text == prevInput) return false; + shiftSelecting = null; + var same = 0, l = Math.min(prevInput.length, text.length); + while (same < l && prevInput[same] == text[same]) ++same; + if (same < prevInput.length) + sel.from = {line: sel.from.line, ch: sel.from.ch - (prevInput.length - same)}; + else if (overwrite && posEq(sel.from, sel.to)) + sel.to = {line: sel.to.line, ch: Math.min(getLine(sel.to.line).text.length, sel.to.ch + (text.length - same))}; + replaceSelection(text.slice(same), "end"); + prevInput = text; + return true; + } + function resetInput(user) { + if (!posEq(sel.from, sel.to)) { + prevInput = ""; + input.value = getSelection(); + selectInput(input); + } else if (user) prevInput = input.value = ""; + } + + function focusInput() { + if (options.readOnly != "nocursor") input.focus(); + } + + function scrollEditorIntoView() { + if (!cursor.getBoundingClientRect) return; + var rect = cursor.getBoundingClientRect(); + // IE returns bogus coordinates when the instance sits inside of an iframe and the cursor is hidden + if (ie && rect.top == rect.bottom) return; + var winH = window.innerHeight || Math.max(document.body.offsetHeight, document.documentElement.offsetHeight); + if (rect.top < 0 || rect.bottom > winH) cursor.scrollIntoView(); + } + function scrollCursorIntoView() { + var cursor = localCoords(sel.inverted ? sel.from : sel.to); + var x = options.lineWrapping ? Math.min(cursor.x, lineSpace.offsetWidth) : cursor.x; + return scrollIntoView(x, cursor.y, x, cursor.yBot); + } + function scrollIntoView(x1, y1, x2, y2) { + var pl = paddingLeft(), pt = paddingTop(); + y1 += pt; y2 += pt; x1 += pl; x2 += pl; + var screen = scroller.clientHeight, screentop = scroller.scrollTop, scrolled = false, result = true; + if (y1 < screentop) {scroller.scrollTop = Math.max(0, y1); scrolled = true;} + else if (y2 > screentop + screen) {scroller.scrollTop = y2 - screen; scrolled = true;} + + var screenw = scroller.clientWidth, screenleft = scroller.scrollLeft; + var gutterw = options.fixedGutter ? gutter.clientWidth : 0; + if (x1 < screenleft + gutterw) { + if (x1 < 50) x1 = 0; + scroller.scrollLeft = Math.max(0, x1 - 10 - gutterw); + scrolled = true; + } + else if (x2 > screenw + screenleft - 3) { + scroller.scrollLeft = x2 + 10 - screenw; + scrolled = true; + if (x2 > code.clientWidth) result = false; + } + if (scrolled && options.onScroll) options.onScroll(instance); + return result; + } + + function visibleLines() { + var lh = textHeight(), top = scroller.scrollTop - paddingTop(); + var from_height = Math.max(0, Math.floor(top / lh)); + var to_height = Math.ceil((top + scroller.clientHeight) / lh); + return {from: lineAtHeight(doc, from_height), + to: lineAtHeight(doc, to_height)}; + } + // Uses a set of changes plus the current scroll position to + // determine which DOM updates have to be made, and makes the + // updates. + function updateDisplay(changes, suppressCallback) { + if (!scroller.clientWidth) { + showingFrom = showingTo = displayOffset = 0; + return; + } + // Compute the new visible window + var visible = visibleLines(); + // Bail out if the visible area is already rendered and nothing changed. + if (changes !== true && changes.length == 0 && visible.from > showingFrom && visible.to < showingTo) return; + var from = Math.max(visible.from - 100, 0), to = Math.min(doc.size, visible.to + 100); + if (showingFrom < from && from - showingFrom < 20) from = showingFrom; + if (showingTo > to && showingTo - to < 20) to = Math.min(doc.size, showingTo); + + // Create a range of theoretically intact lines, and punch holes + // in that using the change info. + var intact = changes === true ? [] : + computeIntact([{from: showingFrom, to: showingTo, domStart: 0}], changes); + // Clip off the parts that won't be visible + var intactLines = 0; + for (var i = 0; i < intact.length; ++i) { + var range = intact[i]; + if (range.from < from) {range.domStart += (from - range.from); range.from = from;} + if (range.to > to) range.to = to; + if (range.from >= range.to) intact.splice(i--, 1); + else intactLines += range.to - range.from; + } + if (intactLines == to - from) return; + intact.sort(function(a, b) {return a.domStart - b.domStart;}); + + var th = textHeight(), gutterDisplay = gutter.style.display; + //lineDiv.style.display = "none"; + patchDisplay(from, to, intact); + lineDiv.style.display = gutter.style.display = ""; + + // Position the mover div to align with the lines it's supposed + // to be showing (which will cover the visible display) + var different = from != showingFrom || to != showingTo || lastSizeC != scroller.clientHeight + th; + // This is just a bogus formula that detects when the editor is + // resized or the font size changes. + if (different) lastSizeC = scroller.clientHeight + th; + showingFrom = from; showingTo = to; + displayOffset = heightAtLine(doc, from); + mover.style.top = (displayOffset * th) + "px"; + if (scroller.clientHeight) + code.style.height = (doc.height * th + 2 * paddingTop()) + "px"; + + // Since this is all rather error prone, it is honoured with the + // only assertion in the whole file. + if (lineDiv.childNodes.length != showingTo - showingFrom) + throw new Error("BAD PATCH! " + JSON.stringify(intact) + " size=" + (showingTo - showingFrom) + + " nodes=" + lineDiv.childNodes.length); + + if (options.lineWrapping) { + maxWidth = scroller.clientWidth; + var curNode = lineDiv.firstChild, heightChanged = false; + doc.iter(showingFrom, showingTo, function(line) { + if (!line.hidden) { + var height = Math.round(curNode.offsetHeight / th) || 1; + if (line.height != height) { + updateLineHeight(line, height); + gutterDirty = heightChanged = true; + } + } + curNode = curNode.nextSibling; + }); + if (heightChanged) + code.style.height = (doc.height * th + 2 * paddingTop()) + "px"; + } else { + if (maxWidth == null) maxWidth = stringWidth(maxLine); + if (maxWidth > scroller.clientWidth) { + lineSpace.style.width = maxWidth + "px"; + // Needed to prevent odd wrapping/hiding of widgets placed in here. + code.style.width = ""; + code.style.width = scroller.scrollWidth + "px"; + } else { + lineSpace.style.width = code.style.width = ""; + } + } + gutter.style.display = gutterDisplay; + if (different || gutterDirty) updateGutter(); + updateSelection(); + if (!suppressCallback && options.onUpdate) options.onUpdate(instance); + return true; + } + + function computeIntact(intact, changes) { + for (var i = 0, l = changes.length || 0; i < l; ++i) { + var change = changes[i], intact2 = [], diff = change.diff || 0; + for (var j = 0, l2 = intact.length; j < l2; ++j) { + var range = intact[j]; + if (change.to <= range.from && change.diff) + intact2.push({from: range.from + diff, to: range.to + diff, + domStart: range.domStart}); + else if (change.to <= range.from || change.from >= range.to) + intact2.push(range); + else { + if (change.from > range.from) + intact2.push({from: range.from, to: change.from, domStart: range.domStart}); + if (change.to < range.to) + intact2.push({from: change.to + diff, to: range.to + diff, + domStart: range.domStart + (change.to - range.from)}); + } + } + intact = intact2; + } + return intact; + } + + function patchDisplay(from, to, intact) { + // The first pass removes the DOM nodes that aren't intact. + if (!intact.length) lineDiv.innerHTML = ""; + else { + function killNode(node) { + var tmp = node.nextSibling; + node.parentNode.removeChild(node); + return tmp; + } + var domPos = 0, curNode = lineDiv.firstChild, n; + for (var i = 0; i < intact.length; ++i) { + var cur = intact[i]; + while (cur.domStart > domPos) {curNode = killNode(curNode); domPos++;} + for (var j = 0, e = cur.to - cur.from; j < e; ++j) {curNode = curNode.nextSibling; domPos++;} + } + while (curNode) curNode = killNode(curNode); + } + // This pass fills in the lines that actually changed. + var nextIntact = intact.shift(), curNode = lineDiv.firstChild, j = from; + var scratch = document.createElement("div"); + var text_height = textHeight(); //Remove this once heights are in pixels instead of lines + doc.iter(from, to, function(line) { + if (nextIntact && nextIntact.to == j) nextIntact = intact.shift(); + if (!nextIntact || nextIntact.from > j) { + if (line.hidden) var html = scratch.innerHTML = "
";
+          else {
+            var html = line.getHTML(makeTab);
+            if (!line.isWidgetBlock) {
+              html = '' + html + '';
+            }
+            // Kludge to make sure the styled element lies behind the selection (by z-index)
+            if (line.bgClassName)
+              html = '
 
' + html + "
"; + } + scratch.innerHTML = html; + var insertChild = scratch.firstChild; + lineDiv.insertBefore(insertChild, curNode); + line.nodeAdded(insertChild); + line.setHeight( insertChild.clientHeight / text_height ); + } else { + curNode = curNode.nextSibling; + } + ++j; + }); + } + + function updateGutter() { + if (!options.gutter && !options.lineNumbers) return; + var hText = mover.offsetHeight, hEditor = scroller.clientHeight; + gutter.style.height = (hText - hEditor < 2 ? hEditor : hText) + "px"; + var html = [], i = showingFrom, normalNode; + doc.iter(showingFrom, Math.max(showingTo, showingFrom + 1), function(line) { + if (line.hidden) { + html.push("
");
+        } else {
+          var marker = line.gutterMarker;
+          var text = options.lineNumbers ? i + options.firstLineNumber : null;
+          if (marker && marker.text)
+            text = marker.text.replace("%N%", text != null ? text : "");
+          else if (text == null)
+            text = "\u00a0";
+          html.push((marker && marker.style ? '
' : "
"), text);
+          for (var j = 1; j < line.height; ++j) html.push("
 "); + html.push("
"); + if (!marker) normalNode = i; + } + ++i; + }); + gutter.style.display = "none"; + gutterText.innerHTML = html.join(""); + // Make sure scrolling doesn't cause number gutter size to pop + if (normalNode != null) { + var node = gutterText.childNodes[normalNode - showingFrom]; + var minwidth = String(doc.size).length, val = eltText(node), pad = ""; + while (val.length + pad.length < minwidth) pad += "\u00a0"; + if (pad) node.insertBefore(document.createTextNode(pad), node.firstChild); + } + gutter.style.display = ""; + lineSpace.style.marginLeft = gutter.offsetWidth + "px"; + gutterDirty = false; + } + function updateSelection() { + var collapsed = posEq(sel.from, sel.to); + var fromPos = localCoords(sel.from, true); + var toPos = collapsed ? fromPos : localCoords(sel.to, true); + var headPos = sel.inverted ? fromPos : toPos, th = textHeight(); + var wrapOff = eltOffset(wrapper), lineOff = eltOffset(lineDiv); + inputDiv.style.top = Math.max(0, Math.min(scroller.offsetHeight, headPos.y + lineOff.top - wrapOff.top)) + "px"; + inputDiv.style.left = Math.max(0, Math.min(scroller.offsetWidth, headPos.x + lineOff.left - wrapOff.left)) + "px"; + if (collapsed) { + cursor.style.top = headPos.y + "px"; + cursor.style.left = (options.lineWrapping ? Math.min(headPos.x, lineSpace.offsetWidth) : headPos.x) + "px"; + cursor.style.display = ""; + selectionDiv.style.display = "none"; + } else { + var sameLine = fromPos.y == toPos.y, html = ""; + function add(left, top, right, height) { + html += '
'; + } + var clientWidth = lineSpace.clientWidth || lineSpace.offsetWidth; + var clientHeight = lineSpace.clientHeight || lineSpace.offsetHeight; + if (sel.from.ch && fromPos.y >= 0) { + var right = sameLine ? clientWidth - toPos.x : 0; + add(fromPos.x, fromPos.y, right, th); + } + var middleStart = Math.max(0, fromPos.y + (sel.from.ch ? th : 0)); + var middleHeight = Math.min(toPos.y, clientHeight) - middleStart; + if (middleHeight > 0.2 * th) + add(0, middleStart, 0, middleHeight); + if ((!sameLine || !sel.from.ch) && toPos.y < clientHeight - .5 * th) + add(0, toPos.y, clientWidth - toPos.x, th); + selectionDiv.innerHTML = html; + cursor.style.display = "none"; + selectionDiv.style.display = ""; + } + } + + function setShift(val) { + if (val) shiftSelecting = shiftSelecting || (sel.inverted ? sel.to : sel.from); + else shiftSelecting = null; + } + function setSelectionUser(from, to) { + var sh = shiftSelecting && clipPos(shiftSelecting); + if (sh) { + if (posLess(sh, from)) from = sh; + else if (posLess(to, sh)) to = sh; + } + setSelection(from, to); + userSelChange = true; + } + // Update the selection. Last two args are only used by + // updateLines, since they have to be expressed in the line + // numbers before the update. + function setSelection(from, to, oldFrom, oldTo) { + goalColumn = null; + if (oldFrom == null) {oldFrom = sel.from.line; oldTo = sel.to.line;} + if (posEq(sel.from, from) && posEq(sel.to, to)) return; + if (posLess(to, from)) {var tmp = to; to = from; from = tmp;} + + // Skip over hidden lines. + if (from.line != oldFrom) { + var from1 = skipHidden(from, oldFrom, sel.from.ch); + // If there is no non-hidden line left, force visibility on current line + if (!from1) setLineHidden(from.line, false); + else from = from1; + } + if (to.line != oldTo) to = skipHidden(to, oldTo, sel.to.ch); + + if (posEq(from, to)) sel.inverted = false; + else if (posEq(from, sel.to)) sel.inverted = false; + else if (posEq(to, sel.from)) sel.inverted = true; + + if (options.autoClearEmptyLines && posEq(sel.from, sel.to)) { + var head = sel.inverted ? from : to; + if (head.line != sel.from.line && sel.from.line < doc.size) { + var oldLine = getLine(sel.from.line); + if (/^\s+$/.test(oldLine.text)) + setTimeout(operation(function() { + if (oldLine.parent && /^\s+$/.test(oldLine.text)) { + var no = lineNo(oldLine); + replaceRange("", {line: no, ch: 0}, {line: no, ch: oldLine.text.length}); + } + }, 10)); + } + } + + sel.from = from; sel.to = to; + selectionChanged = true; + } + function skipHidden(pos, oldLine, oldCh) { + function getNonHidden(dir) { + var lNo = pos.line + dir, end = dir == 1 ? doc.size : -1; + while (lNo != end) { + var line = getLine(lNo); + if (!line.hidden) { + var ch = pos.ch; + if (ch > oldCh || ch > line.text.length) ch = line.text.length; + return {line: lNo, ch: ch}; + } + lNo += dir; + } + } + var line = getLine(pos.line); + if (!line.hidden) return pos; + if (pos.line >= oldLine) return getNonHidden(1) || getNonHidden(-1); + else return getNonHidden(-1) || getNonHidden(1); + } + function setCursor(line, ch, user) { + var pos = clipPos({line: line, ch: ch || 0}); + (user ? setSelectionUser : setSelection)(pos, pos); + } + + function clipLine(n) {return Math.max(0, Math.min(n, doc.size-1));} + function clipPos(pos) { + if (pos.line < 0) return {line: 0, ch: 0}; + if (pos.line >= doc.size) return {line: doc.size-1, ch: getLine(doc.size-1).text.length}; + var ch = pos.ch, linelen = getLine(pos.line).text.length; + if (ch == null || ch > linelen) return {line: pos.line, ch: linelen}; + else if (ch < 0) return {line: pos.line, ch: 0}; + else return pos; + } + + function findPosH(dir, unit) { + var end = sel.inverted ? sel.from : sel.to, line = end.line, ch = end.ch; + var lineObj = getLine(line); + function findNextLine() { + for (var l = line + dir, e = dir < 0 ? -1 : doc.size; l != e; l += dir) { + var lo = getLine(l); + if (!lo.hidden) { line = l; lineObj = lo; return true; } + } + } + function moveOnce(boundToLine) { + if (ch == (dir < 0 ? 0 : lineObj.text.length)) { + if (!boundToLine && findNextLine()) ch = dir < 0 ? lineObj.text.length : 0; + else return false; + } else if (lineObj.isWidgetBlock) { //Select the entire line + ch = dir < 0 ? 0 : lineObj.text.length; + } else ch += dir; + return true; + } + if (unit == "char") moveOnce(); + else if (unit == "column") moveOnce(true); + else if (unit == "word") { + var sawWord = false; + for (;;) { + if (dir < 0) if (!moveOnce()) break; + if (isWordChar(lineObj.text.charAt(ch))) sawWord = true; + else if (sawWord) {if (dir < 0) {dir = 1; moveOnce();} break;} + if (dir > 0) if (!moveOnce()) break; + } + } + return {line: line, ch: ch}; + } + function moveH(dir, unit) { + var pos = dir < 0 ? sel.from : sel.to; + if (shiftSelecting || posEq(sel.from, sel.to)) pos = findPosH(dir, unit); + setCursor(pos.line, pos.ch, true); + } + function deleteH(dir, unit) { + if (!posEq(sel.from, sel.to)) replaceRange("", sel.from, sel.to); + else if (dir < 0) replaceRange("", findPosH(dir, unit), sel.to); + else replaceRange("", sel.from, findPosH(dir, unit)); + userSelChange = true; + } + var goalColumn = null; + function moveV(dir, unit) { + var dist = 0, pos = sel.inverted ? sel.from : sel.to, loc = localCoords(pos, true); + if (goalColumn != null) pos.x = goalColumn; + if (unit == "page") dist = Math.min(scroller.clientHeight, window.innerHeight || document.documentElement.clientHeight); + else if (unit == "line") { + if (dir > 0) { + dist = Math.ceil(getLine(pos.line).height); + } else dist = 1; + dist *= textHeight(); + } + var target = coordsChar(loc.x, loc.y + dist * dir + 2); + if (unit == "page") scroller.scrollTop += localCoords(target, true).y - loc.y; + setCursor(target.line, target.ch, true); + goalColumn = loc.x; + } + + function selectWordAt(pos) { + var line = getLine(pos.line).text; + var start = pos.ch, end = pos.ch; + while (start > 0 && isWordChar(line.charAt(start - 1))) --start; + while (end < line.length && isWordChar(line.charAt(end))) ++end; + setSelectionUser({line: pos.line, ch: start}, {line: pos.line, ch: end}); + } + function selectLine(line) { + setSelectionUser({line: line, ch: 0}, clipPos({line: line + 1, ch: 0})); + } + function indentSelected(mode) { + if (posEq(sel.from, sel.to)) return indentLine(sel.from.line, mode); + var e = sel.to.line - (sel.to.ch ? 0 : 1); + for (var i = sel.from.line; i <= e; ++i) indentLine(i, mode); + } + + function indentLine(n, how) { + if (!how) how = "add"; + if (how == "smart") { + if (!mode.indent) how = "prev"; + else var state = getStateBefore(n); + } + + var line = getLine(n), curSpace = line.indentation(options.tabSize), + curSpaceString = line.text.match(/^\s*/)[0], indentation; + if (how == "prev") { + if (n) indentation = getLine(n-1).indentation(options.tabSize); + else indentation = 0; + } + else if (how == "smart") indentation = mode.indent(state, line.text.slice(curSpaceString.length), line.text); + else if (how == "add") indentation = curSpace + options.indentUnit; + else if (how == "subtract") indentation = curSpace - options.indentUnit; + indentation = Math.max(0, indentation); + var diff = indentation - curSpace; + + if (!diff) { + if (sel.from.line != n && sel.to.line != n) return; + var indentString = curSpaceString; + } + else { + var indentString = "", pos = 0; + if (options.indentWithTabs) + for (var i = Math.floor(indentation / options.tabSize); i; --i) {pos += options.tabSize; indentString += "\t";} + while (pos < indentation) {++pos; indentString += " ";} + } + + replaceRange(indentString, {line: n, ch: 0}, {line: n, ch: curSpaceString.length}); + } + + function loadMode() { + mode = CodeMirror.getMode(options, options.mode); + doc.iter(0, doc.size, function(line) { line.stateAfter = null; }); + work = [0]; + startWorker(); + } + function gutterChanged() { + var visible = options.gutter || options.lineNumbers; + gutter.style.display = visible ? "" : "none"; + if (visible) gutterDirty = true; + else lineDiv.parentNode.style.marginLeft = 0; + } + function wrappingChanged(from, to) { + if (options.lineWrapping) { + wrapper.className += " CodeMirror-wrap"; + var perLine = scroller.clientWidth / charWidth() - 3; + doc.iter(0, doc.size, function(line) { + if (line.hidden) return; + var guess = Math.ceil(line.text.length / perLine) || 1; + if (guess != 1) updateLineHeight(line, guess); + }); + lineSpace.style.width = code.style.width = ""; + } else { + wrapper.className = wrapper.className.replace(" CodeMirror-wrap", ""); + maxWidth = null; maxLine = ""; + doc.iter(0, doc.size, function(line) { + if (line.height != 1 && !line.hidden) updateLineHeight(line, 1); + if (line.text.length > maxLine.length) maxLine = line.text; + }); + } + changes.push({from: 0, to: doc.size}); + } + function makeTab(col) { + var w = options.tabSize - col % options.tabSize, cached = tabCache[w]; + if (cached) return cached; + for (var str = '', i = 0; i < w; ++i) str += " "; + return (tabCache[w] = {html: str + "", width: w}); + } + function themeChanged() { + scroller.className = scroller.className.replace(/\s*cm-s-\w+/g, "") + + options.theme.replace(/(^|\s)\s*/g, " cm-s-"); + } + + function TextMarker() { this.set = []; } + TextMarker.prototype.clear = operation(function() { + var min = Infinity, max = -Infinity; + for (var i = 0, e = this.set.length; i < e; ++i) { + var line = this.set[i], mk = line.marked; + if (!mk || !line.parent) continue; + var lineN = lineNo(line); + min = Math.min(min, lineN); max = Math.max(max, lineN); + for (var j = 0; j < mk.length; ++j) + if (mk[j].marker == this) mk.splice(j--, 1); + } + if (min != Infinity) + changes.push({from: min, to: max + 1}); + }); + TextMarker.prototype.find = function() { + var from, to; + for (var i = 0, e = this.set.length; i < e; ++i) { + var line = this.set[i], mk = line.marked; + for (var j = 0; j < mk.length; ++j) { + var mark = mk[j]; + if (mark.marker == this) { + if (mark.from != null || mark.to != null) { + var found = lineNo(line); + if (found != null) { + if (mark.from != null) from = {line: found, ch: mark.from}; + if (mark.to != null) to = {line: found, ch: mark.to}; + } + } + } + } + } + return {from: from, to: to}; + }; + + function markText(from, to, className) { + from = clipPos(from); to = clipPos(to); + var tm = new TextMarker(); + if (!posLess(from, to)) return tm; + function add(line, from, to, className) { + getLine(line).addMark(new MarkedText(from, to, className, tm)); + } + if (from.line == to.line) add(from.line, from.ch, to.ch, className); + else { + add(from.line, from.ch, null, className); + for (var i = from.line + 1, e = to.line; i < e; ++i) + add(i, null, null, className); + add(to.line, null, to.ch, className); + } + changes.push({from: from.line, to: to.line + 1}); + return tm; + } + + function setBookmark(pos) { + pos = clipPos(pos); + var bm = new Bookmark(pos.ch); + getLine(pos.line).addMark(bm); + return bm; + } + + function findMarksAt(pos) { + pos = clipPos(pos); + var markers = [], marked = getLine(pos.line).marked; + if (!marked) return markers; + for (var i = 0, e = marked.length; i < e; ++i) { + var m = marked[i]; + if ((m.from == null || m.from <= pos.ch) && + (m.to == null || m.to >= pos.ch)) + markers.push(m.marker || m); + } + return markers; + } + + function addGutterMarker(line, text, className) { + if (typeof line == "number") line = getLine(clipLine(line)); + line.gutterMarker = {text: text, style: className}; + gutterDirty = true; + return line; + } + function removeGutterMarker(line) { + if (typeof line == "number") line = getLine(clipLine(line)); + line.gutterMarker = null; + gutterDirty = true; + } + + function changeLine(handle, op) { + var no = handle, line = handle; + if (typeof handle == "number") line = getLine(clipLine(handle)); + else no = lineNo(handle); + if (no == null) return null; + if (op(line, no)) changes.push({from: no, to: no + 1}); + else return null; + return line; + } + function setLineClass(handle, className, bgClassName) { + return changeLine(handle, function(line) { + if (line.className != className || line.bgClassName != bgClassName) { + line.className = className; + line.bgClassName = bgClassName; + return true; + } + }); + } + function setLineHidden(handle, hidden) { + return changeLine(handle, function(line, no) { + if (line.hidden != hidden) { + line.hidden = hidden; + updateLineHeight(line, hidden ? 0 : 1); + var fline = sel.from.line, tline = sel.to.line; + if (hidden && (fline == no || tline == no)) { + var from = fline == no ? skipHidden({line: fline, ch: 0}, fline, 0) : sel.from; + var to = tline == no ? skipHidden({line: tline, ch: 0}, tline, 0) : sel.to; + // Can't hide the last visible line, we'd have no place to put the cursor + if (!to) return; + setSelection(from, to); + } + return (gutterDirty = true); + } + }); + } + + function lineInfo(line) { + if (typeof line == "number") { + if (!isLine(line)) return null; + var n = line; + line = getLine(line); + if (!line) return null; + } + else { + var n = lineNo(line); + if (n == null) return null; + } + var marker = line.gutterMarker; + return {line: n, handle: line, text: line.text, markerText: marker && marker.text, + markerClass: marker && marker.style, lineClass: line.className, bgClass: line.bgClassName}; + } + + function stringWidth(str) { + measure.innerHTML = "
x
"; + measure.firstChild.firstChild.firstChild.nodeValue = str; + return measure.firstChild.firstChild.offsetWidth || 10; + } + // These are used to go from pixel positions to character + // positions, taking varying character widths into account. + function charFromX(line, x) { + if (x <= 0) return 0; + var lineObj = getLine(line), text = lineObj.text; + function getX(len) { + measure.innerHTML = "
" + lineObj.getHTML(makeTab, len) + "
"; + return measure.firstChild.firstChild.offsetWidth; + } + var from = 0, fromX = 0, to = text.length, toX; + // Guess a suitable upper bound for our search. + var estimated = Math.min(to, Math.ceil(x / charWidth())); + for (;;) { + var estX = getX(estimated); + if (estX <= x && estimated < to) estimated = Math.min(to, Math.ceil(estimated * 1.2)); + else {toX = estX; to = estimated; break;} + } + if (x > toX) return to; + // Try to guess a suitable lower bound as well. + estimated = Math.floor(to * 0.8); estX = getX(estimated); + if (estX < x) {from = estimated; fromX = estX;} + // Do a binary search between these bounds. + for (;;) { + if (to - from <= 1) return (toX - x > x - fromX) ? from : to; + var middle = Math.ceil((from + to) / 2), middleX = getX(middle); + if (middleX > x) {to = middle; toX = middleX;} + else {from = middle; fromX = middleX;} + } + } + + var tempId = Math.floor(Math.random() * 0xffffff).toString(16); + function measureLine(line, ch) { + if (ch == 0) return {top: 0, left: 0}; + var extra = ""; + // Include extra text at the end to make sure the measured line is wrapped in the right way. + if (options.lineWrapping) { + var end = line.text.indexOf(" ", ch + 6); + extra = htmlEscape(line.text.slice(ch + 1, end < 0 ? line.text.length : end + (ie ? 5 : 0))); + } + measure.innerHTML = "
" + line.getHTML(makeTab, ch) +
+        '' + htmlEscape(line.text.charAt(ch) || " ") + "" +
+        extra + "
"; + var elt = document.getElementById("CodeMirror-temp-" + tempId); + var top = elt.offsetTop, left = elt.offsetLeft; + // Older IEs report zero offsets for spans directly after a wrap + if (ie && top == 0 && left == 0) { + var backup = document.createElement("span"); + backup.innerHTML = "x"; + elt.parentNode.insertBefore(backup, elt.nextSibling); + top = backup.offsetTop; + } + return {top: top, left: left}; + } + function localCoords(pos, inLineWrap) { + var x, lh = textHeight(), y = lh * (heightAtLine(doc, pos.line) - (inLineWrap ? displayOffset : 0)); + if (pos.ch == 0) x = 0; + else { + var sp = measureLine(getLine(pos.line), pos.ch); + x = sp.left; + if (options.lineWrapping) y += Math.max(0, sp.top); + } + return {x: x, y: y, yBot: y + lh}; + } + // Coords must be lineSpace-local + function coordsChar(x, y) { + if (y < 0) y = 0; + var th = textHeight(), cw = charWidth(), heightPos = displayOffset + y / th; + var lineNo = lineAtHeight(doc, heightPos); + if (lineNo >= doc.size) return {line: doc.size - 1, ch: getLine(doc.size - 1).text.length}; + var lineObj = getLine(lineNo), text = lineObj.text; + var tw = options.lineWrapping, innerOff = tw ? heightPos - heightAtLine(doc, lineNo) : 0; + if (x <= 0 && innerOff == 0) return {line: lineNo, ch: 0}; + function getX(len) { + var sp = measureLine(lineObj, len); + if (tw) { + var off = Math.round(sp.top / th); + return Math.max(0, sp.left + (off - innerOff) * scroller.clientWidth); + } + return sp.left; + } + var from = 0, fromX = 0, to = text.length, toX; + // Guess a suitable upper bound for our search. + var estimated = Math.min(to, Math.ceil((x + innerOff * scroller.clientWidth * .9) / cw)); + for (;;) { + var estX = getX(estimated); + if (estX <= x && estimated < to) estimated = Math.min(to, Math.ceil(estimated * 1.2)); + else {toX = estX; to = estimated; break;} + } + if (x > toX) return {line: lineNo, ch: to}; + // Try to guess a suitable lower bound as well. + estimated = Math.floor(to * 0.8); estX = getX(estimated); + if (estX < x) {from = estimated; fromX = estX;} + // Do a binary search between these bounds. + for (;;) { + if (to - from <= 1) return {line: lineNo, ch: (toX - x > x - fromX) ? from : to}; + var middle = Math.ceil((from + to) / 2), middleX = getX(middle); + if (middleX > x) {to = middle; toX = middleX;} + else {from = middle; fromX = middleX;} + } + } + function pageCoords(pos) { + var local = localCoords(pos, true), off = eltOffset(lineSpace); + return {x: off.left + local.x, y: off.top + local.y, yBot: off.top + local.yBot}; + } + + var cachedHeight, cachedHeightFor, measureText; + function textHeight() { + if (measureText == null) { + measureText = "
";
+        for (var i = 0; i < 49; ++i) measureText += "x
"; + measureText += "x
"; + } + var offsetHeight = lineDiv.clientHeight; + if (offsetHeight == cachedHeightFor) return cachedHeight; + cachedHeightFor = offsetHeight; + measure.innerHTML = measureText; + cachedHeight = measure.firstChild.offsetHeight / 50 || 1; + measure.innerHTML = ""; + return cachedHeight; + } + var cachedWidth, cachedWidthFor = 0; + function charWidth() { + if (scroller.clientWidth == cachedWidthFor) return cachedWidth; + cachedWidthFor = scroller.clientWidth; + return (cachedWidth = stringWidth("x")); + } + function paddingTop() {return lineSpace.offsetTop;} + function paddingLeft() {return lineSpace.offsetLeft;} + + function posFromMouse(e, liberal) { + var offW = eltOffset(scroller, true), x, y; + // Fails unpredictably on IE[67] when mouse is dragged around quickly. + try { x = e.clientX; y = e.clientY; } catch (e) { return null; } + // This is a mess of a heuristic to try and determine whether a + // scroll-bar was clicked or not, and to return null if one was + // (and !liberal). + if (!liberal && (x - offW.left > scroller.clientWidth || y - offW.top > scroller.clientHeight)) + return null; + var offL = eltOffset(lineSpace, true); + return coordsChar(x - offL.left, y - offL.top); + } + function onContextMenu(e) { + var pos = posFromMouse(e), scrollPos = scroller.scrollTop; + if (!pos || window.opera) return; // Opera is difficult. + if (posEq(sel.from, sel.to) || posLess(pos, sel.from) || !posLess(pos, sel.to)) + operation(setCursor)(pos.line, pos.ch); + + var oldCSS = input.style.cssText; + inputDiv.style.position = "absolute"; + input.style.cssText = "position: fixed; width: 30px; height: 30px; top: " + (e.clientY - 5) + + "px; left: " + (e.clientX - 5) + "px; z-index: 1000; background: white; " + + "border-width: 0; outline: none; overflow: hidden; opacity: .05; filter: alpha(opacity=5);"; + leaveInputAlone = true; + var val = input.value = getSelection(); + focusInput(); + selectInput(input); + function rehide() { + var newVal = splitLines(input.value).join("\n"); + if (newVal != val) operation(replaceSelection)(newVal, "end"); + inputDiv.style.position = "relative"; + input.style.cssText = oldCSS; + if (ie_lt9) scroller.scrollTop = scrollPos; + leaveInputAlone = false; + resetInput(true); + slowPoll(); + } + + if (gecko) { + e_stop(e); + var mouseup = connect(window, "mouseup", function() { + mouseup(); + setTimeout(rehide, 20); + }, true); + } else { + setTimeout(rehide, 50); + } + } + + // Cursor-blinking + function restartBlink() { + clearInterval(blinker); + var on = true; + cursor.style.visibility = ""; + blinker = setInterval(function() { + cursor.style.visibility = (on = !on) ? "" : "hidden"; + }, 650); + } + + var matching = {"(": ")>", ")": "(<", "[": "]>", "]": "[<", "{": "}>", "}": "{<"}; + function matchBrackets(autoclear) { + var head = sel.inverted ? sel.from : sel.to, line = getLine(head.line), pos = head.ch - 1; + var match = (pos >= 0 && matching[line.text.charAt(pos)]) || matching[line.text.charAt(++pos)]; + if (!match) return; + var ch = match.charAt(0), forward = match.charAt(1) == ">", d = forward ? 1 : -1, st = line.styles; + for (var off = pos + 1, i = 0, e = st.length; i < e; i+=2) + if ((off -= st[i].length) <= 0) {var style = st[i+1]; break;} + + var stack = [line.text.charAt(pos)], re = /[(){}[\]]/; + function scan(line, from, to) { + if (!line.text) return; + var st = line.styles, pos = forward ? 0 : line.text.length - 1, cur; + for (var i = forward ? 0 : st.length - 2, e = forward ? st.length : -2; i != e; i += 2*d) { + var text = st[i]; + if (st[i+1] != null && st[i+1] != style) {pos += d * text.length; continue;} + for (var j = forward ? 0 : text.length - 1, te = forward ? text.length : -1; j != te; j += d, pos+=d) { + if (pos >= from && pos < to && re.test(cur = text.charAt(j))) { + var match = matching[cur]; + if (match.charAt(1) == ">" == forward) stack.push(cur); + else if (stack.pop() != match.charAt(0)) return {pos: pos, match: false}; + else if (!stack.length) return {pos: pos, match: true}; + } + } + } + } + for (var i = head.line, e = forward ? Math.min(i + 100, doc.size) : Math.max(-1, i - 100); i != e; i+=d) { + var line = getLine(i), first = i == head.line; + var found = scan(line, first && forward ? pos + 1 : 0, first && !forward ? pos : line.text.length); + if (found) break; + } + if (!found) found = {pos: null, match: false}; + var style = found.match ? "CodeMirror-matchingbracket" : "CodeMirror-nonmatchingbracket"; + var one = markText({line: head.line, ch: pos}, {line: head.line, ch: pos+1}, style), + two = found.pos != null && markText({line: i, ch: found.pos}, {line: i, ch: found.pos + 1}, style); + var clear = operation(function(){one.clear(); two && two.clear();}); + if (autoclear) setTimeout(clear, 800); + else bracketHighlighted = clear; + } + + // Finds the line to start with when starting a parse. Tries to + // find a line with a stateAfter, so that it can start with a + // valid state. If that fails, it returns the line with the + // smallest indentation, which tends to need the least context to + // parse correctly. + function findStartLine(n) { + var minindent, minline; + for (var search = n, lim = n - 40; search > lim; --search) { + if (search == 0) return 0; + var line = getLine(search-1); + if (line.stateAfter) return search; + var indented = line.indentation(options.tabSize); + if (minline == null || minindent > indented) { + minline = search - 1; + minindent = indented; + } + } + return minline; + } + function getStateBefore(n) { + var start = findStartLine(n), state = start && getLine(start-1).stateAfter; + if (!state) state = startState(mode); + else state = copyState(mode, state); + doc.iter(start, n, function(line) { + line.highlight(mode, state, options.tabSize); + line.stateAfter = copyState(mode, state); + }); + if (start < n) changes.push({from: start, to: n}); + if (n < doc.size && !getLine(n).stateAfter) work.push(n); + return state; + } + function highlightLines(start, end) { + var state = getStateBefore(start); + doc.iter(start, end, function(line) { + line.highlight(mode, state, options.tabSize); + line.stateAfter = copyState(mode, state); + }); + } + function highlightWorker() { + var end = +new Date + options.workTime; + var foundWork = work.length; + while (work.length) { + if (!getLine(showingFrom).stateAfter) var task = showingFrom; + else var task = work.pop(); + if (task >= doc.size) continue; + var start = findStartLine(task), state = start && getLine(start-1).stateAfter; + if (state) state = copyState(mode, state); + else state = startState(mode); + + var unchanged = 0, compare = mode.compareStates, realChange = false, + i = start, bail = false; + doc.iter(i, doc.size, function(line) { + var hadState = line.stateAfter; + if (+new Date > end) { + work.push(i); + startWorker(options.workDelay); + if (realChange) changes.push({from: task, to: i + 1}); + return (bail = true); + } + var changed = line.highlight(mode, state, options.tabSize); + if (changed) realChange = true; + line.stateAfter = copyState(mode, state); + if (compare) { + if (hadState && compare(hadState, state)) return true; + } else { + if (changed !== false || !hadState) unchanged = 0; + else if (++unchanged > 3 && (!mode.indent || mode.indent(hadState, "") == mode.indent(state, ""))) + return true; + } + ++i; + }); + if (bail) return; + if (realChange) changes.push({from: task, to: i + 1}); + } + if (foundWork && options.onHighlightComplete) + options.onHighlightComplete(instance); + } + function startWorker(time) { + if (!work.length) return; + highlight.set(time, operation(highlightWorker)); + } + + // Operations are used to wrap changes in such a way that each + // change won't have to update the cursor and display (which would + // be awkward, slow, and error-prone), but instead updates are + // batched and then all combined and executed at once. + function startOperation() { + updateInput = userSelChange = textChanged = null; + changes = []; selectionChanged = false; callbacks = []; + } + function endOperation() { + var reScroll = false, updated; + if (selectionChanged) reScroll = !scrollCursorIntoView(); + if (changes.length) updated = updateDisplay(changes, true); + else { + if (selectionChanged) updateSelection(); + if (gutterDirty) updateGutter(); + } + if (reScroll) scrollCursorIntoView(); + if (selectionChanged) {scrollEditorIntoView(); restartBlink();} + + if (focused && !leaveInputAlone && + (updateInput === true || (updateInput !== false && selectionChanged))) + resetInput(userSelChange); + + if (selectionChanged && options.matchBrackets) + setTimeout(operation(function() { + if (bracketHighlighted) {bracketHighlighted(); bracketHighlighted = null;} + if (posEq(sel.from, sel.to)) matchBrackets(false); + }), 20); + var tc = textChanged, cbs = callbacks; // these can be reset by callbacks + if (selectionChanged && options.onCursorActivity) + options.onCursorActivity(instance); + if (tc && options.onChange && instance) + options.onChange(instance, tc); + for (var i = 0; i < cbs.length; ++i) cbs[i](instance); + if (updated && options.onUpdate) options.onUpdate(instance); + } + var nestedOperation = 0; + function operation(f) { + return function() { + if (!nestedOperation++) startOperation(); + try {var result = f.apply(this, arguments);} + finally {if (!--nestedOperation) endOperation();} + return result; + }; + } + + for (var ext in extensions) + if (extensions.propertyIsEnumerable(ext) && + !instance.propertyIsEnumerable(ext)) + instance[ext] = extensions[ext]; + return instance; + } // (end of function CodeMirror) + + // The default configuration options. + CodeMirror.defaults = { + value: "", + mode: null, + theme: "default", + indentUnit: 2, + indentWithTabs: false, + smartIndent: true, + tabSize: 4, + keyMap: "default", + extraKeys: null, + electricChars: true, + autoClearEmptyLines: false, + onKeyEvent: null, + lineWrapping: false, + lineNumbers: false, + gutter: false, + fixedGutter: false, + firstLineNumber: 1, + readOnly: false, + onChange: null, + onCursorActivity: null, + onGutterClick: null, + onHighlightComplete: null, + onUpdate: null, + onFocus: null, onBlur: null, onScroll: null, + matchBrackets: false, + workTime: 100, + workDelay: 200, + pollInterval: 100, + undoDepth: 40, + tabindex: null, + autofocus: null + }; + + var ios = /AppleWebKit/.test(navigator.userAgent) && /Mobile\/\w+/.test(navigator.userAgent); + var mac = ios || /Mac/.test(navigator.platform); + var win = /Win/.test(navigator.platform); + + // Known modes, by name and by MIME + var modes = CodeMirror.modes = {}, mimeModes = CodeMirror.mimeModes = {}; + CodeMirror.defineMode = function(name, mode) { + if (!CodeMirror.defaults.mode && name != "null") CodeMirror.defaults.mode = name; + modes[name] = mode; + }; + CodeMirror.defineMIME = function(mime, spec) { + mimeModes[mime] = spec; + }; + CodeMirror.resolveMode = function(spec) { + if (typeof spec == "string" && mimeModes.hasOwnProperty(spec)) + spec = mimeModes[spec]; + else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+xml$/.test(spec)) + return CodeMirror.resolveMode("application/xml"); + if (typeof spec == "string") return {name: spec}; + else return spec || {name: "null"}; + }; + CodeMirror.getMode = function(options, spec) { + var spec = CodeMirror.resolveMode(spec); + var mfactory = modes[spec.name]; + if (!mfactory) { + if (window.console) console.warn("No mode " + spec.name + " found, falling back to plain text."); + return CodeMirror.getMode(options, "text/plain"); + } + return mfactory(options, spec); + }; + CodeMirror.listModes = function() { + var list = []; + for (var m in modes) + if (modes.propertyIsEnumerable(m)) list.push(m); + return list; + }; + CodeMirror.listMIMEs = function() { + var list = []; + for (var m in mimeModes) + if (mimeModes.propertyIsEnumerable(m)) list.push({mime: m, mode: mimeModes[m]}); + return list; + }; + + var extensions = CodeMirror.extensions = {}; + CodeMirror.defineExtension = function(name, func) { + extensions[name] = func; + }; + + var commands = CodeMirror.commands = { + selectAll: function(cm) {cm.setSelection({line: 0, ch: 0}, {line: cm.lineCount() - 1});}, + killLine: function(cm) { + var from = cm.getCursor(true), to = cm.getCursor(false), sel = !posEq(from, to); + if (!sel && cm.getLine(from.line).length == from.ch) cm.replaceRange("", from, {line: from.line + 1, ch: 0}); + else cm.replaceRange("", from, sel ? to : {line: from.line}); + }, + deleteLine: function(cm) {var l = cm.getCursor().line; cm.replaceRange("", {line: l, ch: 0}, {line: l});}, + undo: function(cm) {cm.undo();}, + redo: function(cm) {cm.redo();}, + goDocStart: function(cm) {cm.setCursor(0, 0, true);}, + goDocEnd: function(cm) {cm.setSelection({line: cm.lineCount() - 1}, null, true);}, + goLineStart: function(cm) {cm.setCursor(cm.getCursor().line, 0, true);}, + goLineStartSmart: function(cm) { + var cur = cm.getCursor(); + var text = cm.getLine(cur.line), firstNonWS = Math.max(0, text.search(/\S/)); + cm.setCursor(cur.line, cur.ch <= firstNonWS && cur.ch ? 0 : firstNonWS, true); + }, + goLineEnd: function(cm) {cm.setSelection({line: cm.getCursor().line}, null, true);}, + goLineUp: function(cm) {cm.moveV(-1, "line");}, + goLineDown: function(cm) {cm.moveV(1, "line");}, + goPageUp: function(cm) {cm.moveV(-1, "page");}, + goPageDown: function(cm) {cm.moveV(1, "page");}, + goCharLeft: function(cm) {cm.moveH(-1, "char");}, + goCharRight: function(cm) {cm.moveH(1, "char");}, + goColumnLeft: function(cm) {cm.moveH(-1, "column");}, + goColumnRight: function(cm) {cm.moveH(1, "column");}, + goWordLeft: function(cm) {cm.moveH(-1, "word");}, + goWordRight: function(cm) {cm.moveH(1, "word");}, + delCharLeft: function(cm) {cm.deleteH(-1, "char");}, + delCharRight: function(cm) {cm.deleteH(1, "char");}, + delWordLeft: function(cm) {cm.deleteH(-1, "word");}, + delWordRight: function(cm) {cm.deleteH(1, "word");}, + indentAuto: function(cm) {cm.indentSelection("smart");}, + indentMore: function(cm) {cm.indentSelection("add");}, + indentLess: function(cm) {cm.indentSelection("subtract");}, + insertTab: function(cm) {cm.replaceSelection("\t", "end");}, + transposeChars: function(cm) { + var cur = cm.getCursor(), line = cm.getLine(cur.line); + if (cur.ch > 0 && cur.ch < line.length - 1) + cm.replaceRange(line.charAt(cur.ch) + line.charAt(cur.ch - 1), + {line: cur.line, ch: cur.ch - 1}, {line: cur.line, ch: cur.ch + 1}); + }, + newlineAndIndent: function(cm) { + cm.replaceSelection("\n", "end"); + cm.indentLine(cm.getCursor().line); + }, + toggleOverwrite: function(cm) {cm.toggleOverwrite();} + }; + + var keyMap = CodeMirror.keyMap = {}; + keyMap.basic = { + "Left": "goCharLeft", "Right": "goCharRight", "Up": "goLineUp", "Down": "goLineDown", + "End": "goLineEnd", "Home": "goLineStartSmart", "PageUp": "goPageUp", "PageDown": "goPageDown", + "Delete": "delCharRight", "Backspace": "delCharLeft", "Tab": "insertTab", "Shift-Tab": "indentAuto", + "Enter": "newlineAndIndent", "Insert": "toggleOverwrite" + }; + // Note that the save and find-related commands aren't defined by + // default. Unknown commands are simply ignored. + keyMap.pcDefault = { + "Ctrl-A": "selectAll", "Ctrl-D": "deleteLine", "Ctrl-Z": "undo", "Shift-Ctrl-Z": "redo", "Ctrl-Y": "redo", + "Ctrl-Home": "goDocStart", "Alt-Up": "goDocStart", "Ctrl-End": "goDocEnd", "Ctrl-Down": "goDocEnd", + "Ctrl-Left": "goWordLeft", "Ctrl-Right": "goWordRight", "Alt-Left": "goLineStart", "Alt-Right": "goLineEnd", + "Ctrl-Backspace": "delWordLeft", "Ctrl-Delete": "delWordRight", "Ctrl-S": "save", "Ctrl-F": "find", + "Ctrl-G": "findNext", "Shift-Ctrl-G": "findPrev", "Shift-Ctrl-F": "replace", "Shift-Ctrl-R": "replaceAll", + "Ctrl-[": "indentLess", "Ctrl-]": "indentMore", + fallthrough: "basic" + }; + keyMap.macDefault = { + "Cmd-A": "selectAll", "Cmd-D": "deleteLine", "Cmd-Z": "undo", "Shift-Cmd-Z": "redo", "Cmd-Y": "redo", + "Cmd-Up": "goDocStart", "Cmd-End": "goDocEnd", "Cmd-Down": "goDocEnd", "Alt-Left": "goWordLeft", + "Alt-Right": "goWordRight", "Cmd-Left": "goLineStart", "Cmd-Right": "goLineEnd", "Alt-Backspace": "delWordLeft", + "Ctrl-Alt-Backspace": "delWordRight", "Alt-Delete": "delWordRight", "Cmd-S": "save", "Cmd-F": "find", + "Cmd-G": "findNext", "Shift-Cmd-G": "findPrev", "Cmd-Alt-F": "replace", "Shift-Cmd-Alt-F": "replaceAll", + "Cmd-[": "indentLess", "Cmd-]": "indentMore", + fallthrough: ["basic", "emacsy"] + }; + keyMap["default"] = mac ? keyMap.macDefault : keyMap.pcDefault; + keyMap.emacsy = { + "Ctrl-F": "goCharRight", "Ctrl-B": "goCharLeft", "Ctrl-P": "goLineUp", "Ctrl-N": "goLineDown", + "Alt-F": "goWordRight", "Alt-B": "goWordLeft", "Ctrl-A": "goLineStart", "Ctrl-E": "goLineEnd", + "Ctrl-V": "goPageUp", "Shift-Ctrl-V": "goPageDown", "Ctrl-D": "delCharRight", "Ctrl-H": "delCharLeft", + "Alt-D": "delWordRight", "Alt-Backspace": "delWordLeft", "Ctrl-K": "killLine", "Ctrl-T": "transposeChars" + }; + + function getKeyMap(val) { + if (typeof val == "string") return keyMap[val]; + else return val; + } + function lookupKey(name, extraMap, map, handle) { + function lookup(map) { + map = getKeyMap(map); + var found = map[name]; + if (found != null && handle(found)) return true; + if (map.catchall) return handle(map.catchall); + var fallthrough = map.fallthrough; + if (fallthrough == null) return false; + if (Object.prototype.toString.call(fallthrough) != "[object Array]") + return lookup(fallthrough); + for (var i = 0, e = fallthrough.length; i < e; ++i) { + if (lookup(fallthrough[i])) return true; + } + return false; + } + if (extraMap && lookup(extraMap)) return true; + return lookup(map); + } + function isModifierKey(event) { + var name = keyNames[e_prop(event, "keyCode")]; + return name == "Ctrl" || name == "Alt" || name == "Shift" || name == "Mod"; + } + + CodeMirror.fromTextArea = function(textarea, options) { + if (!options) options = {}; + options.value = textarea.value; + if (!options.tabindex && textarea.tabindex) + options.tabindex = textarea.tabindex; + if (options.autofocus == null && textarea.getAttribute("autofocus") != null) + options.autofocus = true; + + function save() {textarea.value = instance.getValue();} + if (textarea.form) { + // Deplorable hack to make the submit method do the right thing. + var rmSubmit = connect(textarea.form, "submit", save, true); + if (typeof textarea.form.submit == "function") { + var realSubmit = textarea.form.submit; + function wrappedSubmit() { + save(); + textarea.form.submit = realSubmit; + textarea.form.submit(); + textarea.form.submit = wrappedSubmit; + } + textarea.form.submit = wrappedSubmit; + } + } + + textarea.style.display = "none"; + var instance = CodeMirror(function(node) { + textarea.parentNode.insertBefore(node, textarea.nextSibling); + }, options); + instance.save = save; + instance.getTextArea = function() { return textarea; }; + instance.toTextArea = function() { + save(); + textarea.parentNode.removeChild(instance.getWrapperElement()); + textarea.style.display = ""; + if (textarea.form) { + rmSubmit(); + if (typeof textarea.form.submit == "function") + textarea.form.submit = realSubmit; + } + }; + return instance; + }; + + // Utility functions for working with state. Exported because modes + // sometimes need to do this. + function copyState(mode, state) { + if (state === true) return state; + if (mode.copyState) return mode.copyState(state); + var nstate = {}; + for (var n in state) { + var val = state[n]; + if (val instanceof Array) val = val.concat([]); + nstate[n] = val; + } + return nstate; + } + CodeMirror.copyState = copyState; + function startState(mode, a1, a2) { + return mode.startState ? mode.startState(a1, a2) : true; + } + CodeMirror.startState = startState; + + // The character stream used by a mode's parser. + function StringStream(string, tabSize) { + this.pos = this.start = 0; + this.string = string; + this.tabSize = tabSize || 8; + } + StringStream.prototype = { + eol: function() {return this.pos >= this.string.length;}, + sol: function() {return this.pos == 0;}, + peek: function() {return this.string.charAt(this.pos);}, + next: function() { + if (this.pos < this.string.length) + return this.string.charAt(this.pos++); + }, + eat: function(match) { + var ch = this.string.charAt(this.pos); + if (typeof match == "string") var ok = ch == match; + else var ok = ch && (match.test ? match.test(ch) : match(ch)); + if (ok) {++this.pos; return ch;} + }, + eatWhile: function(match) { + var start = this.pos; + while (this.eat(match)){} + return this.pos > start; + }, + eatSpace: function() { + var start = this.pos; + while (/[\s\u00a0]/.test(this.string.charAt(this.pos))) ++this.pos; + return this.pos > start; + }, + skipToEnd: function() {this.pos = this.string.length;}, + skipTo: function(ch) { + var found = this.string.indexOf(ch, this.pos); + if (found > -1) {this.pos = found; return true;} + }, + backUp: function(n) {this.pos -= n;}, + column: function() {return countColumn(this.string, this.start, this.tabSize);}, + indentation: function() {return countColumn(this.string, null, this.tabSize);}, + match: function(pattern, consume, caseInsensitive) { + if (typeof pattern == "string") { + function cased(str) {return caseInsensitive ? str.toLowerCase() : str;} + if (cased(this.string).indexOf(cased(pattern), this.pos) == this.pos) { + if (consume !== false) this.pos += pattern.length; + return true; + } + } + else { + var match = this.string.slice(this.pos).match(pattern); + if (match && consume !== false) this.pos += match[0].length; + return match; + } + }, + current: function(){return this.string.slice(this.start, this.pos);} + }; + CodeMirror.StringStream = StringStream; + + function MarkedText(from, to, className, marker) { + this.from = from; this.to = to; this.style = className; this.marker = marker; + } + MarkedText.prototype = { + attach: function(line) { this.marker.set.push(line); }, + detach: function(line) { + var ix = indexOf(this.marker.set, line); + if (ix > -1) this.marker.set.splice(ix, 1); + }, + split: function(pos, lenBefore) { + if (this.to <= pos && this.to != null) return null; + var from = this.from < pos || this.from == null ? null : this.from - pos + lenBefore; + var to = this.to == null ? null : this.to - pos + lenBefore; + return new MarkedText(from, to, this.style, this.marker); + }, + dup: function() { return new MarkedText(null, null, this.style, this.marker); }, + clipTo: function(fromOpen, from, toOpen, to, diff) { + if (fromOpen && to > this.from && (to < this.to || this.to == null)) + this.from = null; + else if (this.from != null && this.from >= from) + this.from = Math.max(to, this.from) + diff; + if (toOpen && (from < this.to || this.to == null) && (from > this.from || this.from == null)) + this.to = null; + else if (this.to != null && this.to > from) + this.to = to < this.to ? this.to + diff : from; + }, + isDead: function() { return this.from != null && this.to != null && this.from >= this.to; }, + sameSet: function(x) { return this.marker == x.marker; } + }; + + function Bookmark(pos) { + this.from = pos; this.to = pos; this.line = null; + } + Bookmark.prototype = { + attach: function(line) { this.line = line; }, + detach: function(line) { if (this.line == line) this.line = null; }, + split: function(pos, lenBefore) { + if (pos < this.from) { + this.from = this.to = (this.from - pos) + lenBefore; + return this; + } + }, + isDead: function() { return this.from > this.to; }, + clipTo: function(fromOpen, from, toOpen, to, diff) { + if ((fromOpen || from < this.from) && (toOpen || to > this.to)) { + this.from = 0; this.to = -1; + } else if (this.from > from) { + this.from = this.to = Math.max(to, this.from) + diff; + } + }, + sameSet: function(x) { return false; }, + find: function() { + if (!this.line || !this.line.parent) return null; + return {line: lineNo(this.line), ch: this.from}; + }, + clear: function() { + if (this.line) { + var found = indexOf(this.line.marked, this); + if (found != -1) this.line.marked.splice(found, 1); + this.line = null; + } + } + }; + + // Line objects. These hold state related to a line, including + // highlighting info (the styles array). + function Line(text, styles) { + this.styles = styles || [text, null]; + this.text = text; + this.height = 1; + this.marked = this.gutterMarker = this.className = this.bgClassName = this.handlers = null; + this.stateAfter = this.parent = this.hidden = null; + this.isWidgetBlock = false; + } + Line.inheritMarks = function(text, orig) { + var ln = new Line(text), mk = orig && orig.marked; + if (mk) { + for (var i = 0; i < mk.length; ++i) { + if (mk[i].to == null && mk[i].style) { + var newmk = ln.marked || (ln.marked = []), mark = mk[i]; + var nmark = mark.dup(); newmk.push(nmark); nmark.attach(ln); + } + } + } + return ln; + } + Line.prototype = { + // Replace a piece of a line, keeping the styles around it intact. + replace: function(from, to_, text) { + var st = [], mk = this.marked, to = to_ == null ? this.text.length : to_; + copyStyles(0, from, this.styles, st); + if (text) st.push(text, null); + copyStyles(to, this.text.length, this.styles, st); + this.styles = st; + this.text = this.text.slice(0, from) + text + this.text.slice(to); + this.stateAfter = null; + if (mk) { + var diff = text.length - (to - from); + for (var i = 0; i < mk.length; ++i) { + var mark = mk[i]; + mark.clipTo(from == null, from || 0, to_ == null, to, diff); + if (mark.isDead()) {mark.detach(this); mk.splice(i--, 1);} + } + } + }, + // Split a part off a line, keeping styles and markers intact. + split: function(pos, textBefore) { + var st = [textBefore, null], mk = this.marked; + copyStyles(pos, this.text.length, this.styles, st); + var taken = new Line(textBefore + this.text.slice(pos), st); + if (mk) { + for (var i = 0; i < mk.length; ++i) { + var mark = mk[i]; + var newmark = mark.split(pos, textBefore.length); + if (newmark) { + if (!taken.marked) taken.marked = []; + taken.marked.push(newmark); newmark.attach(taken); + if (newmark == mark) mk.splice(i--, 1); + } + } + } + return taken; + }, + append: function(line) { + var mylen = this.text.length, mk = line.marked, mymk = this.marked; + this.text += line.text; + copyStyles(0, line.text.length, line.styles, this.styles); + if (mymk) { + for (var i = 0; i < mymk.length; ++i) + if (mymk[i].to == null) mymk[i].to = mylen; + } + if (mk && mk.length) { + if (!mymk) this.marked = mymk = []; + outer: for (var i = 0; i < mk.length; ++i) { + var mark = mk[i]; + if (!mark.from) { + for (var j = 0; j < mymk.length; ++j) { + var mymark = mymk[j]; + if (mymark.to == mylen && mymark.sameSet(mark)) { + mymark.to = mark.to == null ? null : mark.to + mylen; + if (mymark.isDead()) { + mymark.detach(this); + mk.splice(i--, 1); + } + continue outer; + } + } + } + mymk.push(mark); + mark.attach(this); + mark.from += mylen; + if (mark.to != null) mark.to += mylen; + } + } + }, + fixMarkEnds: function(other) { + var mk = this.marked, omk = other.marked; + if (!mk) return; + for (var i = 0; i < mk.length; ++i) { + var mark = mk[i], close = mark.to == null; + if (close && omk) { + for (var j = 0; j < omk.length; ++j) + if (omk[j].sameSet(mark)) {close = false; break;} + } + if (close) mark.to = this.text.length; + } + }, + fixMarkStarts: function() { + var mk = this.marked; + if (!mk) return; + for (var i = 0; i < mk.length; ++i) + if (mk[i].from == null) mk[i].from = 0; + }, + addMark: function(mark) { + mark.attach(this); + if (this.marked == null) this.marked = []; + this.marked.push(mark); + this.marked.sort(function(a, b){return (a.from || 0) - (b.from || 0);}); + }, + // Run the given mode's parser over a line, update the styles + // array, which contains alternating fragments of text and CSS + // classes. + highlight: function(mode, state, tabSize) { + var stream = new StringStream(this.text, tabSize), st = this.styles, pos = 0; + var changed = false, curWord = st[0], prevWord; + if (this.text == "" && mode.blankLine) mode.blankLine(state); + while (!stream.eol()) { + var style = mode.token(stream, state); + var substr = this.text.slice(stream.start, stream.pos); + stream.start = stream.pos; + if (pos && st[pos-1] == style) + st[pos-2] += substr; + else if (substr) { + if (!changed && (st[pos+1] != style || (pos && st[pos-2] != prevWord))) changed = true; + st[pos++] = substr; st[pos++] = style; + prevWord = curWord; curWord = st[pos]; + } + // Give up when line is ridiculously long + if (stream.pos > 5000) { + st[pos++] = this.text.slice(stream.pos); st[pos++] = null; + break; + } + } + if (st.length != pos) {st.length = pos; changed = true;} + if (pos && st[pos-2] != prevWord) changed = true; + this.isWidgetBlock = (st.length == 2 && st[1] && typeof st[1] == 'object'); + // Short lines with simple highlights return null, and are + // counted as changed by the driver because they are likely to + // highlight the same way in various contexts. + return changed || (st.length < 5 && this.text.length < 10 ? null : false); + }, + nodeAdded: function(node) { + if (this.isWidgetBlock) this.styles[1].callback(node, this); + //this.setHeight(node.clientHeight); + }, + setHeight: function(newHeight) { + if (this.isWidgetBlock) newHeight = 300 / 14.0; + this.growHeight(newHeight - this.height); + }, + //This is a bottom-up height change. InsertHeight is a top-down height change + growHeight: function(deltaHeight) { + if (this.parent && deltaHeight != 0) { + this.parent.growHeight(deltaHeight); + } + this.height = this.height + deltaHeight; + }, + // Fetch the parser token for a given character. Useful for hacks + // that want to inspect the mode state (say, for completion). + getTokenAt: function(mode, state, ch) { + var txt = this.text, stream = new StringStream(txt); + while (stream.pos < ch && !stream.eol()) { + stream.start = stream.pos; + var style = mode.token(stream, state); + } + return {start: stream.start, + end: stream.pos, + string: stream.current(), + className: style || null, + state: state}; + }, + indentation: function(tabSize) {return countColumn(this.text, null, tabSize);}, + // Produces an HTML fragment for the line, taking selection, + // marking, and highlighting into account. + getHTML: function(makeTab, endAt) { + var html = [], first = true, col = 0; + function span(text, style) { + if (!text) return; + // Work around a bug where, in some compat modes, IE ignores leading spaces + if (first && ie && text.charAt(0) == " ") text = "\u00a0" + text.slice(1); + first = false; + if (text.indexOf("\t") == -1) { + col += text.length; + var escaped = htmlEscape(text); + } else { + var escaped = ""; + for (var pos = 0;;) { + var idx = text.indexOf("\t", pos); + if (idx == -1) { + escaped += htmlEscape(text.slice(pos)); + col += text.length - pos; + break; + } else { + col += idx - pos; + var tab = makeTab(col); + escaped += htmlEscape(text.slice(pos, idx)) + tab.html; + col += tab.width; + pos = idx + 1; + } + } + } + if (style) html.push('', escaped, ""); + else html.push(escaped); + } + var st = this.styles, allText = this.text, marked = this.marked; + var len = allText.length; + if (this.isWidgetBlock) return st[1].creator(allText); + if (endAt != null) len = Math.min(endAt, len); + function styleToClass(style) { + if (!style) return null; + return "cm-" + style.replace(/ +/g, " cm-"); + } + if (!allText && endAt == null) + span(" "); + else if (!marked || !marked.length) + for (var i = 0, ch = 0; ch < len; i+=2) { + var str = st[i], style = st[i+1], l = str.length; + if (ch + l > len) str = str.slice(0, len - ch); + ch += l; + span(str, styleToClass(style)); + } + else { + var pos = 0, i = 0, text = "", style, sg = 0; + var nextChange = marked[0].from || 0, marks = [], markpos = 0; + function advanceMarks() { + var m; + while (markpos < marked.length && + ((m = marked[markpos]).from == pos || m.from == null)) { + if (m.style != null) marks.push(m); + ++markpos; + } + nextChange = markpos < marked.length ? marked[markpos].from : Infinity; + for (var i = 0; i < marks.length; ++i) { + var to = marks[i].to || Infinity; + if (to == pos) marks.splice(i--, 1); + else nextChange = Math.min(to, nextChange); + } + } + var m = 0; + while (pos < len) { + if (nextChange == pos) advanceMarks(); + var upto = Math.min(len, nextChange); + while (true) { + if (text) { + var end = pos + text.length; + var appliedStyle = style; + for (var j = 0; j < marks.length; ++j) + appliedStyle = (appliedStyle ? appliedStyle + " " : "") + marks[j].style; + span(end > upto ? text.slice(0, upto - pos) : text, appliedStyle); + if (end >= upto) {text = text.slice(upto - pos); pos = upto; break;} + pos = end; + } + text = st[i++]; style = styleToClass(st[i++]); + } + } + } + return html.join(""); + }, + cleanUp: function() { + this.parent = null; + if (this.marked) + for (var i = 0, e = this.marked.length; i < e; ++i) this.marked[i].detach(this); + } + }; + // Utility used by replace and split above + function copyStyles(from, to, source, dest) { + for (var i = 0, pos = 0, state = 0; pos < to; i+=2) { + var part = source[i], end = pos + part.length; + if (state == 0) { + if (end > from) dest.push(part.slice(from - pos, Math.min(part.length, to - pos)), source[i+1]); + if (end >= from) state = 1; + } + else if (state == 1) { + if (end > to) dest.push(part.slice(0, to - pos), source[i+1]); + else dest.push(part, source[i+1]); + } + pos = end; + } + } + + // Data structure that holds the sequence of lines. + function LeafChunk(lines) { + this.lines = lines; + this.parent = null; + for (var i = 0, e = lines.length, height = 0; i < e; ++i) { + lines[i].parent = this; + height += lines[i].height; + } + this.height = height; + } + LeafChunk.prototype = { + chunkSize: function() { return this.lines.length; }, + remove: function(at, n, callbacks) { + for (var i = at, e = at + n; i < e; ++i) { + var line = this.lines[i]; + this.height -= line.height; + line.cleanUp(); + if (line.handlers) + for (var j = 0; j < line.handlers.length; ++j) callbacks.push(line.handlers[j]); + } + this.lines.splice(at, n); + }, + collapse: function(lines) { + lines.splice.apply(lines, [lines.length, 0].concat(this.lines)); + }, + //This is a bottom-up height change. InsertHeight is a top-down height change + growHeight: function(deltaHeight) { + if (this.parent && deltaHeight != 0) { + this.parent.growHeight(deltaHeight) + } + this.height = this.height + deltaHeight; + }, + insertHeight: function(at, lines, height) { + this.height += height; + this.lines.splice.apply(this.lines, [at, 0].concat(lines)); + for (var i = 0, e = lines.length; i < e; ++i) lines[i].parent = this; + }, + iterN: function(at, n, op) { + for (var e = at + n; at < e; ++at) + if (op(this.lines[at])) return true; + } + }; + function BranchChunk(children) { + this.children = children; + var size = 0, height = 0; + for (var i = 0, e = children.length; i < e; ++i) { + var ch = children[i]; + size += ch.chunkSize(); height += ch.height; + ch.parent = this; + } + this.size = size; + this.height = height; + this.parent = null; + } + BranchChunk.prototype = { + chunkSize: function() { return this.size; }, + remove: function(at, n, callbacks) { + this.size -= n; + for (var i = 0; i < this.children.length; ++i) { + var child = this.children[i], sz = child.chunkSize(); + if (at < sz) { + var rm = Math.min(n, sz - at), oldHeight = child.height; + child.remove(at, rm, callbacks); + this.height -= oldHeight - child.height; + if (sz == rm) { this.children.splice(i--, 1); child.parent = null; } + if ((n -= rm) == 0) break; + at = 0; + } else at -= sz; + } + if (this.size - n < 25) { + var lines = []; + this.collapse(lines); + this.children = [new LeafChunk(lines)]; + this.children[0].parent = this; + } + }, + collapse: function(lines) { + for (var i = 0, e = this.children.length; i < e; ++i) this.children[i].collapse(lines); + }, + insert: function(at, lines) { + var height = 0; + for (var i = 0, e = lines.length; i < e; ++i) height += lines[i].height; + this.insertHeight(at, lines, height); + }, + insertHeight: function(at, lines, height) { + this.size += lines.length; + this.height += height; + for (var i = 0, e = this.children.length; i < e; ++i) { + var child = this.children[i], sz = child.chunkSize(); + if (at <= sz) { + child.insertHeight(at, lines, height); + if (child.lines && child.lines.length > 50) { + while (child.lines.length > 50) { + var spilled = child.lines.splice(child.lines.length - 25, 25); + var newleaf = new LeafChunk(spilled); + child.height -= newleaf.height; + this.children.splice(i + 1, 0, newleaf); + newleaf.parent = this; + } + this.maybeSpill(); + } + break; + } + at -= sz; + } + }, + //This is a bottom-up height change. InsertHeight is a top-down height change + growHeight: function(deltaHeight) { + if (this.parent && deltaHeight != 0) { + this.parent.growHeight(deltaHeight) + } + this.height = this.height + deltaHeight; + }, + maybeSpill: function() { + if (this.children.length <= 10) return; + var me = this; + do { + var spilled = me.children.splice(me.children.length - 5, 5); + var sibling = new BranchChunk(spilled); + if (!me.parent) { // Become the parent node + var copy = new BranchChunk(me.children); + copy.parent = me; + me.children = [copy, sibling]; + me = copy; + } else { + me.size -= sibling.size; + me.height -= sibling.height; + var myIndex = indexOf(me.parent.children, me); + me.parent.children.splice(myIndex + 1, 0, sibling); + } + sibling.parent = me.parent; + } while (me.children.length > 10); + me.parent.maybeSpill(); + }, + iter: function(from, to, op) { this.iterN(from, to - from, op); }, + iterN: function(at, n, op) { + for (var i = 0, e = this.children.length; i < e; ++i) { + var child = this.children[i], sz = child.chunkSize(); + if (at < sz) { + var used = Math.min(n, sz - at); + if (child.iterN(at, used, op)) return true; + if ((n -= used) == 0) break; + at = 0; + } else at -= sz; + } + } + }; + + function getLineAt(chunk, n) { + while (!chunk.lines) { + for (var i = 0;; ++i) { + var child = chunk.children[i], sz = child.chunkSize(); + if (n < sz) { chunk = child; break; } + n -= sz; + } + } + return chunk.lines[n]; + } + function lineNo(line) { + if (line.parent == null) return null; + var cur = line.parent, no = indexOf(cur.lines, line); + for (var chunk = cur.parent; chunk; cur = chunk, chunk = chunk.parent) { + for (var i = 0, e = chunk.children.length; ; ++i) { + if (chunk.children[i] == cur) break; + no += chunk.children[i].chunkSize(); + } + } + return no; + } + function lineAtHeight(chunk, h) { + var n = 0; + outer: do { + for (var i = 0, e = chunk.children.length; i < e; ++i) { + var child = chunk.children[i], ch = child.height; + if (h < ch) { chunk = child; continue outer; } + h -= ch; + n += child.chunkSize(); + } + return n; + } while (!chunk.lines); + for (var i = 0, e = chunk.lines.length; i < e; ++i) { + var line = chunk.lines[i], lh = line.height; + if (h < lh) break; + h -= lh; + } + return n + i; + } + function heightAtLine(chunk, n) { + var h = 0; + outer: do { + for (var i = 0, e = chunk.children.length; i < e; ++i) { + var child = chunk.children[i], sz = child.chunkSize(); + if (n < sz) { chunk = child; continue outer; } + n -= sz; + h += child.height; + } + return h; + } while (!chunk.lines); + for (var i = 0; i < n; ++i) h += chunk.lines[i].height; + return h; + } + + // The history object 'chunks' changes that are made close together + // and at almost the same time into bigger undoable units. + function History() { + this.time = 0; + this.done = []; this.undone = []; + } + History.prototype = { + addChange: function(start, added, old) { + this.undone.length = 0; + var time = +new Date, cur = this.done[this.done.length - 1], last = cur && cur[cur.length - 1]; + var dtime = time - this.time; + if (dtime > 400 || !last) { + this.done.push([{start: start, added: added, old: old}]); + } else if (last.start > start + old.length || last.start + last.added < start - last.added + last.old.length) { + cur.push({start: start, added: added, old: old}); + } else { + var oldoff = 0; + if (start < last.start) { + for (var i = last.start - start - 1; i >= 0; --i) + last.old.unshift(old[i]); + oldoff = Math.min(0, added - old.length); + last.added += last.start - start + oldoff; + last.start = start; + } else if (last.start < start) { + oldoff = start - last.start; + added += oldoff; + } + for (var i = last.added - oldoff, e = old.length; i < e; ++i) + last.old.push(old[i]); + if (last.added < added) last.added = added; + } + this.time = time; + } + }; + + function stopMethod() {e_stop(this);} + // Ensure an event has a stop method. + function addStop(event) { + if (!event.stop) event.stop = stopMethod; + return event; + } + + function e_preventDefault(e) { + if (e.preventDefault) e.preventDefault(); + else e.returnValue = false; + } + function e_stopPropagation(e) { + if (e.stopPropagation) e.stopPropagation(); + else e.cancelBubble = true; + } + function e_stop(e) {e_preventDefault(e); e_stopPropagation(e);} + CodeMirror.e_stop = e_stop; + CodeMirror.e_preventDefault = e_preventDefault; + CodeMirror.e_stopPropagation = e_stopPropagation; + + function e_target(e) {return e.target || e.srcElement;} + function e_button(e) { + if (e.which) return e.which; + else if (e.button & 1) return 1; + else if (e.button & 2) return 3; + else if (e.button & 4) return 2; + } + + // Allow 3rd-party code to override event properties by adding an override + // object to an event object. + function e_prop(e, prop) { + var overridden = e.override && e.override.hasOwnProperty(prop); + return overridden ? e.override[prop] : e[prop]; + } + + // Event handler registration. If disconnect is true, it'll return a + // function that unregisters the handler. + function connect(node, type, handler, disconnect) { + if (typeof node.addEventListener == "function") { + node.addEventListener(type, handler, false); + if (disconnect) return function() {node.removeEventListener(type, handler, false);}; + } + else { + var wrapHandler = function(event) {handler(event || window.event);}; + node.attachEvent("on" + type, wrapHandler); + if (disconnect) return function() {node.detachEvent("on" + type, wrapHandler);}; + } + } + CodeMirror.connect = connect; + + function Delayed() {this.id = null;} + Delayed.prototype = {set: function(ms, f) {clearTimeout(this.id); this.id = setTimeout(f, ms);}}; + + var Pass = CodeMirror.Pass = {toString: function(){return "CodeMirror.Pass";}}; + + var gecko = /gecko\/\d{7}/i.test(navigator.userAgent); + var ie = /MSIE \d/.test(navigator.userAgent); + var ie_lt9 = /MSIE [1-8]\b/.test(navigator.userAgent); + var webkit = /WebKit\//.test(navigator.userAgent); + var chrome = /Chrome\//.test(navigator.userAgent); + var khtml = /KHTML\//.test(navigator.userAgent); + + // Detect drag-and-drop + var dragAndDrop = function() { + // There is *some* kind of drag-and-drop support in IE6-8, but I + // couldn't get it to work yet. + if (ie_lt9) return false; + var div = document.createElement('div'); + return "draggable" in div || "dragDrop" in div; + }(); + + var lineSep = "\n"; + // Feature-detect whether newlines in textareas are converted to \r\n + (function () { + var te = document.createElement("textarea"); + te.value = "foo\nbar"; + if (te.value.indexOf("\r") > -1) lineSep = "\r\n"; + }()); + + // Counts the column offset in a string, taking tabs into account. + // Used mostly to find indentation. + function countColumn(string, end, tabSize) { + if (end == null) { + end = string.search(/[^\s\u00a0]/); + if (end == -1) end = string.length; + } + for (var i = 0, n = 0; i < end; ++i) { + if (string.charAt(i) == "\t") n += tabSize - (n % tabSize); + else ++n; + } + return n; + } + + function computedStyle(elt) { + if (elt.currentStyle) return elt.currentStyle; + return window.getComputedStyle(elt, null); + } + + // Find the position of an element by following the offsetParent chain. + // If screen==true, it returns screen (rather than page) coordinates. + function eltOffset(node, screen) { + var bod = node.ownerDocument.body; + var x = 0, y = 0, skipBody = false; + for (var n = node; n; n = n.offsetParent) { + var ol = n.offsetLeft, ot = n.offsetTop; + // Firefox reports weird inverted offsets when the body has a border. + if (n == bod) { x += Math.abs(ol); y += Math.abs(ot); } + else { x += ol, y += ot; } + if (screen && computedStyle(n).position == "fixed") + skipBody = true; + } + var e = screen && !skipBody ? null : bod; + for (var n = node.parentNode; n != e; n = n.parentNode) + if (n.scrollLeft != null) { x -= n.scrollLeft; y -= n.scrollTop;} + return {left: x, top: y}; + } + // Use the faster and saner getBoundingClientRect method when possible. + if (document.documentElement.getBoundingClientRect != null) eltOffset = function(node, screen) { + // Take the parts of bounding client rect that we are interested in so we are able to edit if need be, + // since the returned value cannot be changed externally (they are kept in sync as the element moves within the page) + try { var box = node.getBoundingClientRect(); box = { top: box.top, left: box.left }; } + catch(e) { box = {top: 0, left: 0}; } + if (!screen) { + // Get the toplevel scroll, working around browser differences. + if (window.pageYOffset == null) { + var t = document.documentElement || document.body.parentNode; + if (t.scrollTop == null) t = document.body; + box.top += t.scrollTop; box.left += t.scrollLeft; + } else { + box.top += window.pageYOffset; box.left += window.pageXOffset; + } + } + return box; + }; + + // Get a node's text content. + function eltText(node) { + return node.textContent || node.innerText || node.nodeValue || ""; + } + function selectInput(node) { + if (ios) { // Mobile Safari apparently has a bug where select() is broken. + node.selectionStart = 0; + node.selectionEnd = node.value.length; + } else node.select(); + } + + // Operations on {line, ch} objects. + function posEq(a, b) {return a.line == b.line && a.ch == b.ch;} + function posLess(a, b) {return a.line < b.line || (a.line == b.line && a.ch < b.ch);} + function copyPos(x) {return {line: x.line, ch: x.ch};} + + var escapeElement = document.createElement("pre"); + function htmlEscape(str) { + escapeElement.textContent = str; + return escapeElement.innerHTML; + } + // Recent (late 2011) Opera betas insert bogus newlines at the start + // of the textContent, so we strip those. + if (htmlEscape("a") == "\na") + htmlEscape = function(str) { + escapeElement.textContent = str; + return escapeElement.innerHTML.slice(1); + }; + // Some IEs don't preserve tabs through innerHTML + else if (htmlEscape("\t") != "\t") + htmlEscape = function(str) { + escapeElement.innerHTML = ""; + escapeElement.appendChild(document.createTextNode(str)); + return escapeElement.innerHTML; + }; + CodeMirror.htmlEscape = htmlEscape; + + // Used to position the cursor after an undo/redo by finding the + // last edited character. + function editEnd(from, to) { + if (!to) return 0; + if (!from) return to.length; + for (var i = from.length, j = to.length; i >= 0 && j >= 0; --i, --j) + if (from.charAt(i) != to.charAt(j)) break; + return j + 1; + } + + function indexOf(collection, elt) { + if (collection.indexOf) return collection.indexOf(elt); + for (var i = 0, e = collection.length; i < e; ++i) + if (collection[i] == elt) return i; + return -1; + } + function isWordChar(ch) { + return /\w/.test(ch) || ch.toUpperCase() != ch.toLowerCase(); + } + + // See if "".split is the broken IE version, if so, provide an + // alternative way to split lines. + var splitLines = "\n\nb".split(/\n/).length != 3 ? function(string) { + var pos = 0, nl, result = []; + while ((nl = string.indexOf("\n", pos)) > -1) { + result.push(string.slice(pos, string.charAt(nl-1) == "\r" ? nl - 1 : nl)); + pos = nl + 1; + } + result.push(string.slice(pos)); + return result; + } : function(string){return string.split(/\r?\n/);}; + CodeMirror.splitLines = splitLines; + + var hasSelection = window.getSelection ? function(te) { + try { return te.selectionStart != te.selectionEnd; } + catch(e) { return false; } + } : function(te) { + try {var range = te.ownerDocument.selection.createRange();} + catch(e) {} + if (!range || range.parentElement() != te) return false; + return range.compareEndPoints("StartToEnd", range) != 0; + }; + + CodeMirror.defineMode("null", function() { + return {token: function(stream) {stream.skipToEnd();}}; + }); + CodeMirror.defineMIME("text/plain", "null"); + + var keyNames = {3: "Enter", 8: "Backspace", 9: "Tab", 13: "Enter", 16: "Shift", 17: "Ctrl", 18: "Alt", + 19: "Pause", 20: "CapsLock", 27: "Esc", 32: "Space", 33: "PageUp", 34: "PageDown", 35: "End", + 36: "Home", 37: "Left", 38: "Up", 39: "Right", 40: "Down", 44: "PrintScrn", 45: "Insert", + 46: "Delete", 59: ";", 91: "Mod", 92: "Mod", 93: "Mod", 127: "Delete", 186: ";", 187: "=", 188: ",", + 189: "-", 190: ".", 191: "/", 192: "`", 219: "[", 220: "\\", 221: "]", 222: "'", 63276: "PageUp", + 63277: "PageDown", 63275: "End", 63273: "Home", 63234: "Left", 63232: "Up", 63235: "Right", + 63233: "Down", 63302: "Insert", 63272: "Delete"}; + CodeMirror.keyNames = keyNames; + (function() { + // Number keys + for (var i = 0; i < 10; i++) keyNames[i + 48] = String(i); + // Alphabetic keys + for (var i = 65; i <= 90; i++) keyNames[i] = String.fromCharCode(i); + // Function keys + for (var i = 1; i <= 12; i++) keyNames[i + 111] = keyNames[i + 63235] = "F" + i; + })(); + + return CodeMirror; +})(); diff --git a/static/js/CodeMirror/mitx_markdown.js b/static/js/CodeMirror/mitx_markdown.js new file mode 100644 index 0000000000..02d6e609c2 --- /dev/null +++ b/static/js/CodeMirror/mitx_markdown.js @@ -0,0 +1,345 @@ +$(function(){ + $(document).ready(function() { + $("a[rel*=leanModal]").leanModal(); + + $("body").append('
'); + + //This is the editor that pops up as a modal + var editorCircuit = $("#schematic_editor").get(0); + //This is the circuit that they last clicked. The one being edited. + var editingCircuit = null; + //Notice we use live, because new circuits can be inserted + $(".schematic_open").live("click", function() { + //Find the new editingCircuit. Transfer its contents to the editorCircuit + editingCircuit = $(this).children("input.schematic").get(0); + + editingCircuit.schematic.update_value(); + var circuit_so_far = $(editingCircuit).val(); + + var n = editorCircuit.schematic.components.length; + for (var i = 0; i < n; i++) + editorCircuit.schematic.components[n - 1 - i].remove(); + + editorCircuit.schematic.load_schematic(circuit_so_far, ""); + }); + + $("#circuit_save_btn").click(function () { + //Take the circuit from the editor and put it back into editingCircuit + editorCircuit.schematic.update_value(); + var saving_circuit = $(editorCircuit).val(); + + var n = editingCircuit.schematic.components.length; + for (var i = 0; i < n; i++) + editingCircuit.schematic.components[n - 1 - i].remove(); + + editingCircuit.schematic.load_schematic(saving_circuit, ""); + + if (editingCircuit.codeMirrorLine) { + editingCircuit.codeMirrorLine.text = "circuit-schematic:" + saving_circuit; + } + + $(".modal_close").first().click(); + }); + }); +}); + +CodeMirror.defineMode("mitx_markdown", function(cmCfg, modeCfg) { + + var htmlMode = CodeMirror.getMode(cmCfg, { name: 'xml', htmlMode: true }); + + var header = 'header' + , code = 'comment' + , quote = 'quote' + , list = 'string' + , hr = 'hr' + , linktext = 'link' + , linkhref = 'string' + , em = 'em' + , strong = 'strong' + , emstrong = 'emstrong'; + + var circuit_formatter = { + creator: function(text) { + var circuit_value = text.match(circuitRE)[1] + + //TODO: We need real html escaping here + circuit_value = CodeMirror.htmlEscape(circuit_value);// circuit_value.replace("\"", "'"); + + var html = ""; + + return html; + }, + callback: function(node, line) { + update_schematics(); + var schmInput = node.firstChild; + schmInput.codeMirrorLine = line; + $(node).leanModal(); + } + }; + + var hrRE = /^[*-=_]/ + , ulRE = /^[*-+]\s+/ + , olRE = /^[0-9]+\.\s+/ + , headerRE = /^(?:\={3,}|-{3,})$/ + , codeRE = /^(k:\t|\s{4,})/ + , textRE = /^[^\[*_\\<>`]+/ + , circuitRE = /^circuit-schematic:(.*)$/; + + function switchInline(stream, state, f) { + state.f = state.inline = f; + return f(stream, state); + } + + function switchBlock(stream, state, f) { + state.f = state.block = f; + return f(stream, state); + } + + + // Blocks + + function blockNormal(stream, state) { + var match; + if (stream.match(circuitRE)) { + stream.skipToEnd(); + return circuit_formatter; + } else if (stream.match(codeRE)) { + stream.skipToEnd(); + return code; + } else if (stream.eatSpace()) { + return null; + } else if (stream.peek() === '#' || stream.match(headerRE)) { + state.header = true; + } else if (stream.eat('>')) { + state.indentation++; + state.quote = true; + } else if (stream.peek() === '[') { + return switchInline(stream, state, footnoteLink); + } else if (hrRE.test(stream.peek())) { + var re = new RegExp('(?:\s*['+stream.peek()+']){3,}$'); + if (stream.match(re, true)) { + return hr; + } + } else if (match = stream.match(ulRE, true) || stream.match(olRE, true)) { + state.indentation += match[0].length; + return list; + } + + return switchInline(stream, state, state.inline); + } + + function htmlBlock(stream, state) { + var style = htmlMode.token(stream, state.htmlState); + if (style === 'tag' && state.htmlState.type !== 'openTag' && !state.htmlState.context) { + state.f = inlineNormal; + state.block = blockNormal; + } + return style; + } + + + // Inline + function getType(state) { + + // Set defaults + returnValue = ''; + + // Strong / Emphasis + if(state.strong){ + if(state.em){ + returnValue += (returnValue ? ' ' : '') + emstrong; + } else { + returnValue += (returnValue ? ' ' : '') + strong; + } + } else { + if(state.em){ + returnValue += (returnValue ? ' ' : '') + em; + } + } + + // Header + if(state.header){ + returnValue += (returnValue ? ' ' : '') + header; + } + + // Quotes + if(state.quote){ + returnValue += (returnValue ? ' ' : '') + quote; + } + + // Check valud and return + if(!returnValue){ + returnValue = null; + } + return returnValue; + + } + + function handleText(stream, state) { + if (stream.match(textRE, true)) { + return getType(state); + } + return undefined; + } + + function inlineNormal(stream, state) { + var style = state.text(stream, state) + if (typeof style !== 'undefined') + return style; + + var ch = stream.next(); + + if (ch === '\\') { + stream.next(); + return getType(state); + } + if (ch === '`') { + return switchInline(stream, state, inlineElement(code, '`')); + } + if (ch === '[') { + return switchInline(stream, state, linkText); + } + if (ch === '<' && stream.match(/^\w/, false)) { + stream.backUp(1); + return switchBlock(stream, state, htmlBlock); + } + + var t = getType(state); + if (ch === '*' || ch === '_') { + if (stream.eat(ch)) { + return (state.strong = !state.strong) ? getType(state) : t; + } + return (state.em = !state.em) ? getType(state) : t; + } + + return getType(state); + } + + function linkText(stream, state) { + while (!stream.eol()) { + var ch = stream.next(); + if (ch === '\\') stream.next(); + if (ch === ']') { + state.inline = state.f = linkHref; + return linktext; + } + } + return linktext; + } + + function linkHref(stream, state) { + stream.eatSpace(); + var ch = stream.next(); + if (ch === '(' || ch === '[') { + return switchInline(stream, state, inlineElement(linkhref, ch === '(' ? ')' : ']')); + } + return 'error'; + } + + function footnoteLink(stream, state) { + if (stream.match(/^[^\]]*\]:/, true)) { + state.f = footnoteUrl; + return linktext; + } + return switchInline(stream, state, inlineNormal); + } + + function footnoteUrl(stream, state) { + stream.eatSpace(); + stream.match(/^[^\s]+/, true); + state.f = state.inline = inlineNormal; + return linkhref; + } + + function inlineRE(endChar) { + if (!inlineRE[endChar]) { + // match any not-escaped-non-endChar and any escaped char + // then match endChar or eol + inlineRE[endChar] = new RegExp('^(?:[^\\\\\\' + endChar + ']|\\\\.)*(?:\\' + endChar + '|$)'); + } + return inlineRE[endChar]; + } + + function inlineElement(type, endChar, next) { + next = next || inlineNormal; + return function(stream, state) { + stream.match(inlineRE(endChar)); + state.inline = state.f = next; + return type; + }; + } + + return { + startState: function() { + return { + f: blockNormal, + + block: blockNormal, + htmlState: htmlMode.startState(), + indentation: 0, + + inline: inlineNormal, + text: handleText, + em: false, + strong: false, + header: false, + quote: false + }; + }, + + copyState: function(s) { + return { + f: s.f, + + block: s.block, + htmlState: CodeMirror.copyState(htmlMode, s.htmlState), + indentation: s.indentation, + + inline: s.inline, + text: s.text, + em: s.em, + strong: s.strong, + header: s.header, + quote: s.quote + }; + }, + + token: function(stream, state) { + if (stream.sol()) { + // Reset EM state + state.em = false; + // Reset STRONG state + state.strong = false; + // Reset state.header + state.header = false; + // Reset state.quote + state.quote = false; + + state.f = state.block; + var previousIndentation = state.indentation + , currentIndentation = 0; + while (previousIndentation > 0) { + if (stream.eat(' ')) { + previousIndentation--; + currentIndentation++; + } else if (previousIndentation >= 4 && stream.eat('\t')) { + previousIndentation -= 4; + currentIndentation += 4; + } else { + break; + } + } + state.indentation = currentIndentation; + + if (currentIndentation > 0) return null; + } + return state.f(stream, state); + }, + + getType: getType + }; + +}); + +CodeMirror.defineMIME("text/x-markdown", "markdown"); diff --git a/static/js/CodeMirror/xml.js b/static/js/CodeMirror/xml.js new file mode 100644 index 0000000000..f467bddccc --- /dev/null +++ b/static/js/CodeMirror/xml.js @@ -0,0 +1,267 @@ +CodeMirror.defineMode("xml", function(config, parserConfig) { + var indentUnit = config.indentUnit; + var Kludges = parserConfig.htmlMode ? { + autoSelfClosers: {"br": true, "img": true, "hr": true, "link": true, "input": true, + "meta": true, "col": true, "frame": true, "base": true, "area": true}, + doNotIndent: {"pre": true}, + allowUnquoted: true, + allowMissing: false + } : {autoSelfClosers: {}, doNotIndent: {}, allowUnquoted: false, allowMissing: false}; + var alignCDATA = parserConfig.alignCDATA; + + // Return variables for tokenizers + var tagName, type; + + function inText(stream, state) { + function chain(parser) { + state.tokenize = parser; + return parser(stream, state); + } + + var ch = stream.next(); + if (ch == "<") { + if (stream.eat("!")) { + if (stream.eat("[")) { + if (stream.match("CDATA[")) return chain(inBlock("atom", "]]>")); + else return null; + } + else if (stream.match("--")) return chain(inBlock("comment", "-->")); + else if (stream.match("DOCTYPE", true, true)) { + stream.eatWhile(/[\w\._\-]/); + return chain(doctype(1)); + } + else return null; + } + else if (stream.eat("?")) { + stream.eatWhile(/[\w\._\-]/); + state.tokenize = inBlock("meta", "?>"); + return "meta"; + } + else { + type = stream.eat("/") ? "closeTag" : "openTag"; + stream.eatSpace(); + tagName = ""; + var c; + while ((c = stream.eat(/[^\s\u00a0=<>\"\'\/?]/))) tagName += c; + state.tokenize = inTag; + return "tag"; + } + } + else if (ch == "&") { + var ok; + if (stream.eat("#")) { + if (stream.eat("x")) { + ok = stream.eatWhile(/[a-fA-F\d]/) && stream.eat(";"); + } else { + ok = stream.eatWhile(/[\d]/) && stream.eat(";"); + } + } else { + ok = stream.eatWhile(/[\w\.\-:]/) && stream.eat(";"); + } + return ok ? "atom" : "error"; + } + else { + stream.eatWhile(/[^&<]/); + return null; + } + } + + function inTag(stream, state) { + var ch = stream.next(); + if (ch == ">" || (ch == "/" && stream.eat(">"))) { + state.tokenize = inText; + type = ch == ">" ? "endTag" : "selfcloseTag"; + return "tag"; + } + else if (ch == "=") { + type = "equals"; + return null; + } + else if (/[\'\"]/.test(ch)) { + state.tokenize = inAttribute(ch); + return state.tokenize(stream, state); + } + else { + stream.eatWhile(/[^\s\u00a0=<>\"\'\/?]/); + return "word"; + } + } + + function inAttribute(quote) { + return function(stream, state) { + while (!stream.eol()) { + if (stream.next() == quote) { + state.tokenize = inTag; + break; + } + } + return "string"; + }; + } + + function inBlock(style, terminator) { + return function(stream, state) { + while (!stream.eol()) { + if (stream.match(terminator)) { + state.tokenize = inText; + break; + } + stream.next(); + } + return style; + }; + } + function doctype(depth) { + return function(stream, state) { + var ch; + while ((ch = stream.next()) != null) { + if (ch == "<") { + state.tokenize = doctype(depth + 1); + return state.tokenize(stream, state); + } else if (ch == ">") { + if (depth == 1) { + state.tokenize = inText; + break; + } else { + state.tokenize = doctype(depth - 1); + return state.tokenize(stream, state); + } + } + } + return "meta"; + }; + } + + var curState, setStyle; + function pass() { + for (var i = arguments.length - 1; i >= 0; i--) curState.cc.push(arguments[i]); + } + function cont() { + pass.apply(null, arguments); + return true; + } + + function pushContext(tagName, startOfLine) { + var noIndent = Kludges.doNotIndent.hasOwnProperty(tagName) || (curState.context && curState.context.noIndent); + curState.context = { + prev: curState.context, + tagName: tagName, + indent: curState.indented, + startOfLine: startOfLine, + noIndent: noIndent + }; + } + function popContext() { + if (curState.context) curState.context = curState.context.prev; + } + + function element(type) { + if (type == "openTag") { + curState.tagName = tagName; + return cont(attributes, endtag(curState.startOfLine)); + } else if (type == "closeTag") { + var err = false; + if (curState.context) { + err = curState.context.tagName != tagName; + } else { + err = true; + } + if (err) setStyle = "error"; + return cont(endclosetag(err)); + } + return cont(); + } + function endtag(startOfLine) { + return function(type) { + if (type == "selfcloseTag" || + (type == "endTag" && Kludges.autoSelfClosers.hasOwnProperty(curState.tagName.toLowerCase()))) + return cont(); + if (type == "endTag") {pushContext(curState.tagName, startOfLine); return cont();} + return cont(); + }; + } + function endclosetag(err) { + return function(type) { + if (err) setStyle = "error"; + if (type == "endTag") { popContext(); return cont(); } + setStyle = "error"; + return cont(arguments.callee); + } + } + + function attributes(type) { + if (type == "word") {setStyle = "attribute"; return cont(attribute, attributes);} + if (type == "endTag" || type == "selfcloseTag") return pass(); + setStyle = "error"; + return cont(attributes); + } + function attribute(type) { + if (type == "equals") return cont(attvalue, attributes); + if (!Kludges.allowMissing) setStyle = "error"; + return (type == "endTag" || type == "selfcloseTag") ? pass() : cont(); + } + function attvalue(type) { + if (type == "string") return cont(attvaluemaybe); + if (type == "word" && Kludges.allowUnquoted) {setStyle = "string"; return cont();} + setStyle = "error"; + return (type == "endTag" || type == "selfCloseTag") ? pass() : cont(); + } + function attvaluemaybe(type) { + if (type == "string") return cont(attvaluemaybe); + else return pass(); + } + + return { + startState: function() { + return {tokenize: inText, cc: [], indented: 0, startOfLine: true, tagName: null, context: null}; + }, + + token: function(stream, state) { + if (stream.sol()) { + state.startOfLine = true; + state.indented = stream.indentation(); + } + if (stream.eatSpace()) return null; + + setStyle = type = tagName = null; + var style = state.tokenize(stream, state); + state.type = type; + if ((style || type) && style != "comment") { + curState = state; + while (true) { + var comb = state.cc.pop() || element; + if (comb(type || style)) break; + } + } + state.startOfLine = false; + return setStyle || style; + }, + + indent: function(state, textAfter, fullLine) { + var context = state.context; + if ((state.tokenize != inTag && state.tokenize != inText) || + context && context.noIndent) + return fullLine ? fullLine.match(/^(\s*)/)[0].length : 0; + if (alignCDATA && / --> - + <%! from django.core.urlresolvers import reverse diff --git a/templates/simplewiki_edit.html b/templates/simplewiki_edit.html index 435870071d..a8c36e247c 100644 --- a/templates/simplewiki_edit.html +++ b/templates/simplewiki_edit.html @@ -9,22 +9,54 @@ <%block name="wiki_head"> + + + + + + + + + + + + From a858c91ffca3902b1699e4d95e1301aa803b0f50 Mon Sep 17 00:00:00 2001 From: Bridger Maxwell Date: Mon, 26 Mar 2012 11:03:14 -0700 Subject: [PATCH 03/50] Simple css change to get a decent looking demo. --- templates/simplewiki_edit.html | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/templates/simplewiki_edit.html b/templates/simplewiki_edit.html index a8c36e247c..8b40689aef 100644 --- a/templates/simplewiki_edit.html +++ b/templates/simplewiki_edit.html @@ -19,13 +19,14 @@ From 5580c62ee4696d140dcc973bb4717c22f42222f8 Mon Sep 17 00:00:00 2001 From: Bridger Maxwell Date: Mon, 26 Mar 2012 13:48:11 -0700 Subject: [PATCH 04/50] Wiki editor now has wrapped lines. --- static/js/CodeMirror/codemirror.js | 88 +++++++++++---------------- static/js/CodeMirror/mitx_markdown.js | 3 + templates/simplewiki_edit.html | 3 +- 3 files changed, 39 insertions(+), 55 deletions(-) diff --git a/static/js/CodeMirror/codemirror.js b/static/js/CodeMirror/codemirror.js index c7d3c86899..b6d2d21bdd 100644 --- a/static/js/CodeMirror/codemirror.js +++ b/static/js/CodeMirror/codemirror.js @@ -693,31 +693,6 @@ var CodeMirror = (function() { if (nlines > 1) doc.remove(from.line + 1, nlines - 1, callbacks); doc.insert(from.line + 1, added); } - if (options.lineWrapping) { - var perLine = scroller.clientWidth / charWidth() - 3; - doc.iter(from.line, from.line + newText.length, function(line) { - if (line.hidden) return; - var guess = Math.ceil(line.text.length / perLine) || 1; - if (guess != line.height) updateLineHeight(line, guess); - }); - } else { - doc.iter(from.line, i + newText.length, function(line) { - var l = line.text; - if (l.length > maxLineLength) { - maxLine = l; maxLineLength = l.length; maxWidth = null; - recomputeMaxLength = false; - } - }); - if (recomputeMaxLength) { - maxLineLength = 0; maxLine = ""; maxWidth = null; - doc.iter(0, doc.size, function(line) { - var l = line.text; - if (l.length > maxLineLength) { - maxLineLength = l.length; maxLine = l; - } - }); - } - } // Add these lines to the work array, so that they will be // highlighted. Adjust work lines if lines were added/removed. @@ -740,6 +715,36 @@ var CodeMirror = (function() { cur.next = changeObj; } else textChanged = changeObj; + + if (options.lineWrapping) { + var perLine = scroller.clientWidth / charWidth() - 3; + doc.iter(from.line, from.line + newText.length, function(line) { + if (line.hidden) return; + var guess = Math.ceil(line.text.length / perLine) || 1; + if (line.isWidgetBlock) + guess = line.styles[1].size(line.text).height / textHeight(); + if (guess != line.height) updateLineHeight(line, guess); + }); + } else { + //TODO: update height here for widget blocks + doc.iter(from.line, i + newText.length, function(line) { + var l = line.text; + if (l.length > maxLineLength) { + maxLine = l; maxLineLength = l.length; maxWidth = null; + recomputeMaxLength = false; + } + }); + if (recomputeMaxLength) { + maxLineLength = 0; maxLine = ""; maxWidth = null; + doc.iter(0, doc.size, function(line) { + var l = line.text; + if (l.length > maxLineLength) { + maxLineLength = l.length; maxLine = l; + } + }); + } + } + // Update the selection function updateLine(n) {return n <= Math.min(to.line, to.line + lendiff) ? n : n + lendiff;} setSelection(selFrom, selTo, updateLine(sel.from.line), updateLine(sel.to.line)); @@ -938,7 +943,7 @@ var CodeMirror = (function() { intact.sort(function(a, b) {return a.domStart - b.domStart;}); var th = textHeight(), gutterDisplay = gutter.style.display; - //lineDiv.style.display = "none"; + lineDiv.style.display = "none"; patchDisplay(from, to, intact); lineDiv.style.display = gutter.style.display = ""; @@ -964,8 +969,9 @@ var CodeMirror = (function() { maxWidth = scroller.clientWidth; var curNode = lineDiv.firstChild, heightChanged = false; doc.iter(showingFrom, showingTo, function(line) { - if (!line.hidden) { + if (!line.hidden && !line.isWidgetBlock) { //TODO: We should handle widget blocks better here var height = Math.round(curNode.offsetHeight / th) || 1; + if (line.isWidgetBlock) height = line.styles[1].size(line.text).height / textHeight(); if (line.height != height) { updateLineHeight(line, height); gutterDirty = heightChanged = true; @@ -1055,7 +1061,6 @@ var CodeMirror = (function() { var insertChild = scratch.firstChild; lineDiv.insertBefore(insertChild, curNode); line.nodeAdded(insertChild); - line.setHeight( insertChild.clientHeight / text_height ); } else { curNode = curNode.nextSibling; } @@ -1354,7 +1359,7 @@ var CodeMirror = (function() { wrapper.className = wrapper.className.replace(" CodeMirror-wrap", ""); maxWidth = null; maxLine = ""; doc.iter(0, doc.size, function(line) { - if (line.height != 1 && !line.hidden) updateLineHeight(line, 1); + if (line.height != 1 && !line.hidden && !line.isWidgetBlock) updateLineHeight(line, 1); if (line.text.length > maxLine.length) maxLine = line.text; }); } @@ -2403,17 +2408,6 @@ var CodeMirror = (function() { if (this.isWidgetBlock) this.styles[1].callback(node, this); //this.setHeight(node.clientHeight); }, - setHeight: function(newHeight) { - if (this.isWidgetBlock) newHeight = 300 / 14.0; - this.growHeight(newHeight - this.height); - }, - //This is a bottom-up height change. InsertHeight is a top-down height change - growHeight: function(deltaHeight) { - if (this.parent && deltaHeight != 0) { - this.parent.growHeight(deltaHeight); - } - this.height = this.height + deltaHeight; - }, // Fetch the parser token for a given character. Useful for hacks // that want to inspect the mode state (say, for completion). getTokenAt: function(mode, state, ch) { @@ -2562,13 +2556,6 @@ var CodeMirror = (function() { collapse: function(lines) { lines.splice.apply(lines, [lines.length, 0].concat(this.lines)); }, - //This is a bottom-up height change. InsertHeight is a top-down height change - growHeight: function(deltaHeight) { - if (this.parent && deltaHeight != 0) { - this.parent.growHeight(deltaHeight) - } - this.height = this.height + deltaHeight; - }, insertHeight: function(at, lines, height) { this.height += height; this.lines.splice.apply(this.lines, [at, 0].concat(lines)); @@ -2643,13 +2630,6 @@ var CodeMirror = (function() { at -= sz; } }, - //This is a bottom-up height change. InsertHeight is a top-down height change - growHeight: function(deltaHeight) { - if (this.parent && deltaHeight != 0) { - this.parent.growHeight(deltaHeight) - } - this.height = this.height + deltaHeight; - }, maybeSpill: function() { if (this.children.length <= 10) return; var me = this; diff --git a/static/js/CodeMirror/mitx_markdown.js b/static/js/CodeMirror/mitx_markdown.js index 02d6e609c2..9a29b65fe0 100644 --- a/static/js/CodeMirror/mitx_markdown.js +++ b/static/js/CodeMirror/mitx_markdown.js @@ -70,6 +70,9 @@ CodeMirror.defineMode("mitx_markdown", function(cmCfg, modeCfg) { return html; }, + size: function(text) { + return {width: 400, height:302}; + }, callback: function(node, line) { update_schematics(); var schmInput = node.firstChild; diff --git a/templates/simplewiki_edit.html b/templates/simplewiki_edit.html index 8b40689aef..82c0456473 100644 --- a/templates/simplewiki_edit.html +++ b/templates/simplewiki_edit.html @@ -38,7 +38,8 @@ var editor = CodeMirror.fromTextArea(document.getElementById("id_contents"), { mode: 'mitx_markdown', matchBrackets: true, - theme: "default" + theme: "default", + lineWrapping: true, }); //Store the inital contents so we can compare for unsaved changes From 952b75496630597d688ec8f43ecf8ffbea49f8f0 Mon Sep 17 00:00:00 2001 From: Bridger Maxwell Date: Mon, 26 Mar 2012 17:15:45 -0700 Subject: [PATCH 05/50] New method works without line wrapping too --- static/js/CodeMirror/codemirror.js | 10 +++++++--- templates/simplewiki_edit.html | 2 +- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/static/js/CodeMirror/codemirror.js b/static/js/CodeMirror/codemirror.js index b6d2d21bdd..bc5927b831 100644 --- a/static/js/CodeMirror/codemirror.js +++ b/static/js/CodeMirror/codemirror.js @@ -697,8 +697,8 @@ var CodeMirror = (function() { // Add these lines to the work array, so that they will be // highlighted. Adjust work lines if lines were added/removed. var newWork = [], lendiff = newText.length - nlines - 1; - for (var i = 0, l = work.length; i < l; ++i) { - var task = work[i]; + for (var j = 0, l = work.length; j < l; ++j) { + var task = work[j]; if (task < from.line) newWork.push(task); else if (task > to.line) newWork.push(task + lendiff); } @@ -727,12 +727,16 @@ var CodeMirror = (function() { }); } else { //TODO: update height here for widget blocks - doc.iter(from.line, i + newText.length, function(line) { + doc.iter(from.line, from.line + newText.length, function(line) { var l = line.text; if (l.length > maxLineLength) { maxLine = l; maxLineLength = l.length; maxWidth = null; recomputeMaxLength = false; } + if (line.isWidgetBlock) { + var guess = line.styles[1].size(line.text).height / textHeight(); + if (guess != line.height) updateLineHeight(line, guess); + } }); if (recomputeMaxLength) { maxLineLength = 0; maxLine = ""; maxWidth = null; diff --git a/templates/simplewiki_edit.html b/templates/simplewiki_edit.html index 82c0456473..1c5ebd40fc 100644 --- a/templates/simplewiki_edit.html +++ b/templates/simplewiki_edit.html @@ -39,7 +39,7 @@ mode: 'mitx_markdown', matchBrackets: true, theme: "default", - lineWrapping: true, + lineWrapping: false, }); //Store the inital contents so we can compare for unsaved changes From 43318b72134e40a8ed5348d82c624b0ae7fc554e Mon Sep 17 00:00:00 2001 From: Bridger Maxwell Date: Tue, 27 Mar 2012 14:33:24 -0700 Subject: [PATCH 06/50] Fixing deletion / insertion bugs and inconsistencies --- static/js/CodeMirror/codemirror.js | 51 ++++++++++++++++++--------- static/js/CodeMirror/mitx_markdown.js | 8 ++--- templates/simplewiki_edit.html | 10 +++--- 3 files changed, 44 insertions(+), 25 deletions(-) diff --git a/static/js/CodeMirror/codemirror.js b/static/js/CodeMirror/codemirror.js index bc5927b831..a34d520891 100644 --- a/static/js/CodeMirror/codemirror.js +++ b/static/js/CodeMirror/codemirror.js @@ -616,6 +616,24 @@ var CodeMirror = (function() { // Afterwards, set the selection to selFrom, selTo. function updateLines(from, to, newText, selFrom, selTo) { if (suppressEdits) return; + + if (from.ch > 0 && newText[0] != '' && getLine(from.line).isWidgetBlock) { + newText.unshift(''); + var widgetLine = getLine(from.line); + function moveSel(sel) { + if (sel.line == from.line && sel.ch > 0){ + return {line: sel.line + 1, ch: sel.ch - widgetLine.text.length}; + } else if (sel.line > from.line) { + return {line: sel.line + 1, ch: sel.ch}; + } + } + selFrom = moveSel(selFrom, widgetLine); + selTo = moveSel(selTo, widgetLine); + } + if (to.ch == 0 && newText[newText.length - 1] != '' && getLine(to.line).isWidgetBlock) { + newText.push(''); + } + if (history) { var old = []; doc.iter(from.line, to.line + 1, function(line) { old.push(line.text); }); @@ -726,7 +744,6 @@ var CodeMirror = (function() { if (guess != line.height) updateLineHeight(line, guess); }); } else { - //TODO: update height here for widget blocks doc.iter(from.line, from.line + newText.length, function(line) { var l = line.text; if (l.length > maxLineLength) { @@ -779,23 +796,11 @@ var CodeMirror = (function() { return end; } function replaceSelection(code, collapse) { - var reposition = false; - if (code.length > 0) { - var fromLine = getLine(sel.from.line), toLine = getLine(sel.to.line); - if (fromLine.isWidgetBlock && sel.from.ch == fromLine.text.length) { - code = "\n" + code; - } else if (toLine.isWidgetBlock && sel.to.ch == 0) { - code = code + "\n"; - reposition = true; - } - } - replaceRange1(splitLines(code), sel.from, sel.to, function(end) { if (collapse == "end") return {from: end, to: end}; else if (collapse == "start") return {from: sel.from, to: sel.from}; else return {from: sel.from, to: end}; }); - if (reposition) moveH(-1, "char"); } function replaceRange1(code, from, to, computeSel) { var endch = code.length == 1 ? code[0].length + from.ch : code[code.length-1].length; @@ -1266,9 +1271,19 @@ var CodeMirror = (function() { setCursor(pos.line, pos.ch, true); } function deleteH(dir, unit) { - if (!posEq(sel.from, sel.to)) replaceRange("", sel.from, sel.to); - else if (dir < 0) replaceRange("", findPosH(dir, unit), sel.to); - else replaceRange("", sel.from, findPosH(dir, unit)); + var from = sel.from; + var to = sel.to; + if (posEq(sel.from, sel.to)) { + if (dir < 0) { + from = findPosH(dir, unit); + if (getLine(from.line).isWidgetBlock) from.ch = 0; + } + else { + to = findPosH(dir, unit); + if (getLine(to.line).isWidgetBlock) to.ch = getLine(to.line).text.length; + } + } + replaceRange("", from, to); userSelChange = true; } var goalColumn = null; @@ -1554,6 +1569,10 @@ var CodeMirror = (function() { var tempId = Math.floor(Math.random() * 0xffffff).toString(16); function measureLine(line, ch) { if (ch == 0) return {top: 0, left: 0}; + if (line.isWidgetBlock) { + var size = line.styles[1].size(line.text); + return {top: -1, left: size.width}; + } var extra = ""; // Include extra text at the end to make sure the measured line is wrapped in the right way. if (options.lineWrapping) { diff --git a/static/js/CodeMirror/mitx_markdown.js b/static/js/CodeMirror/mitx_markdown.js index 9a29b65fe0..aa63967c58 100644 --- a/static/js/CodeMirror/mitx_markdown.js +++ b/static/js/CodeMirror/mitx_markdown.js @@ -2,7 +2,7 @@ $(function(){ $(document).ready(function() { $("a[rel*=leanModal]").leanModal(); - $("body").append('
'); + $("body").append('
'); //This is the editor that pops up as a modal var editorCircuit = $("#schematic_editor").get(0); @@ -65,13 +65,13 @@ CodeMirror.defineMode("mitx_markdown", function(cmCfg, modeCfg) { //TODO: We need real html escaping here circuit_value = CodeMirror.htmlEscape(circuit_value);// circuit_value.replace("\"", "'"); - var html = ""; + var html = "" + + ""; return html; }, size: function(text) { - return {width: 400, height:302}; + return {width: 150, height:154}; }, callback: function(node, line) { update_schematics(); diff --git a/templates/simplewiki_edit.html b/templates/simplewiki_edit.html index 1c5ebd40fc..75e1fec325 100644 --- a/templates/simplewiki_edit.html +++ b/templates/simplewiki_edit.html @@ -45,11 +45,11 @@ //Store the inital contents so we can compare for unsaved changes var initial_contents = editor.getValue(); - window.onbeforeunload = function askConfirm() { //Warn the user before they navigate away - if ( editor.getValue() != initial_contents ) { - return "You have made changes to the article that have not been saved yet."; - } - }; + // window.onbeforeunload = function askConfirm() { //Warn the user before they navigate away + // if ( editor.getValue() != initial_contents ) { + // return "You have made changes to the article that have not been saved yet."; + // } + // }; $("#submit_edit").click(function() { initial_contents = editor.getValue(); From 32384c8eb9f3694c0e8cc261c4b0973780f33431 Mon Sep 17 00:00:00 2001 From: Bridger Maxwell Date: Wed, 28 Mar 2012 00:36:06 -0700 Subject: [PATCH 07/50] Fixed a lot of bugs with selection in codemirror. Seems to work pretty solidly now\! --- static/js/CodeMirror/codemirror.js | 62 +++++++++++++++------------ static/js/CodeMirror/mitx_markdown.js | 2 +- 2 files changed, 35 insertions(+), 29 deletions(-) diff --git a/static/js/CodeMirror/codemirror.js b/static/js/CodeMirror/codemirror.js index a34d520891..261070d6af 100644 --- a/static/js/CodeMirror/codemirror.js +++ b/static/js/CodeMirror/codemirror.js @@ -498,17 +498,17 @@ var CodeMirror = (function() { if (!bound) return false; } var prevShift = shiftSelecting; - try { + // try { //TODO: Reenable this try/catch if (options.readOnly) suppressEdits = true; if (dropShift) shiftSelecting = null; bound(instance); - } catch(e) { - if (e != Pass) throw e; - return false; - } finally { - shiftSelecting = prevShift; - suppressEdits = false; - } + // } catch(e) { + // if (e != Pass) throw e; + // return false; + // } finally { + // shiftSelecting = prevShift; + // suppressEdits = false; + // } return true; } function handleKeyBinding(e) { @@ -617,7 +617,7 @@ var CodeMirror = (function() { function updateLines(from, to, newText, selFrom, selTo) { if (suppressEdits) return; - if (from.ch > 0 && newText[0] != '' && getLine(from.line).isWidgetBlock) { + if (from.ch > 0 && (newText[0] != '' || newText.length == 1) && getLine(from.line).widgetFunction) { newText.unshift(''); var widgetLine = getLine(from.line); function moveSel(sel) { @@ -630,7 +630,7 @@ var CodeMirror = (function() { selFrom = moveSel(selFrom, widgetLine); selTo = moveSel(selTo, widgetLine); } - if (to.ch == 0 && newText[newText.length - 1] != '' && getLine(to.line).isWidgetBlock) { + if (to.ch == 0 && (newText[newText.length - 1] != '' || newText.length == 1) && getLine(to.line).widgetFunction) { newText.push(''); } @@ -739,8 +739,8 @@ var CodeMirror = (function() { doc.iter(from.line, from.line + newText.length, function(line) { if (line.hidden) return; var guess = Math.ceil(line.text.length / perLine) || 1; - if (line.isWidgetBlock) - guess = line.styles[1].size(line.text).height / textHeight(); + if (line.widgetFunction) + guess = line.widgetFunction.size(line.text).height / textHeight(); if (guess != line.height) updateLineHeight(line, guess); }); } else { @@ -750,8 +750,8 @@ var CodeMirror = (function() { maxLine = l; maxLineLength = l.length; maxWidth = null; recomputeMaxLength = false; } - if (line.isWidgetBlock) { - var guess = line.styles[1].size(line.text).height / textHeight(); + if (line.widgetFunction) { + var guess = line.widgetFunction.size(line.text).height / textHeight(); if (guess != line.height) updateLineHeight(line, guess); } }); @@ -978,9 +978,9 @@ var CodeMirror = (function() { maxWidth = scroller.clientWidth; var curNode = lineDiv.firstChild, heightChanged = false; doc.iter(showingFrom, showingTo, function(line) { - if (!line.hidden && !line.isWidgetBlock) { //TODO: We should handle widget blocks better here + if (!line.hidden && !line.widgetFunction) { //TODO: We should handle widget blocks better here var height = Math.round(curNode.offsetHeight / th) || 1; - if (line.isWidgetBlock) height = line.styles[1].size(line.text).height / textHeight(); + if (line.widgetFunction) height = line.widgetFunction.size(line.text).height / textHeight(); if (line.height != height) { updateLineHeight(line, height); gutterDirty = heightChanged = true; @@ -1058,7 +1058,7 @@ var CodeMirror = (function() { if (line.hidden) var html = scratch.innerHTML = "
";
           else {
             var html = line.getHTML(makeTab);
-            if (!line.isWidgetBlock) {
+            if (!line.widgetFunction) {
               html = '' + html + '
'; } // Kludge to make sure the styled element lies behind the selection (by z-index) @@ -1228,7 +1228,8 @@ var CodeMirror = (function() { function clipPos(pos) { if (pos.line < 0) return {line: 0, ch: 0}; if (pos.line >= doc.size) return {line: doc.size-1, ch: getLine(doc.size-1).text.length}; - var ch = pos.ch, linelen = getLine(pos.line).text.length; + var ch = pos.ch, line = getLine(pos.line), linelen =line.text.length; + if (line.widgetFunction && ch != 0) return {line: pos.line, ch: linelen}; if (ch == null || ch > linelen) return {line: pos.line, ch: linelen}; else if (ch < 0) return {line: pos.line, ch: 0}; else return pos; @@ -1247,7 +1248,7 @@ var CodeMirror = (function() { if (ch == (dir < 0 ? 0 : lineObj.text.length)) { if (!boundToLine && findNextLine()) ch = dir < 0 ? lineObj.text.length : 0; else return false; - } else if (lineObj.isWidgetBlock) { //Select the entire line + } else if (lineObj.widgetFunction) { //Select the entire line ch = dir < 0 ? 0 : lineObj.text.length; } else ch += dir; return true; @@ -1276,11 +1277,11 @@ var CodeMirror = (function() { if (posEq(sel.from, sel.to)) { if (dir < 0) { from = findPosH(dir, unit); - if (getLine(from.line).isWidgetBlock) from.ch = 0; + if (getLine(from.line).widgetFunction) from.ch = 0; } else { to = findPosH(dir, unit); - if (getLine(to.line).isWidgetBlock) to.ch = getLine(to.line).text.length; + if (getLine(to.line).widgetFunction) to.ch = getLine(to.line).text.length; } } replaceRange("", from, to); @@ -1378,7 +1379,7 @@ var CodeMirror = (function() { wrapper.className = wrapper.className.replace(" CodeMirror-wrap", ""); maxWidth = null; maxLine = ""; doc.iter(0, doc.size, function(line) { - if (line.height != 1 && !line.hidden && !line.isWidgetBlock) updateLineHeight(line, 1); + if (line.height != 1 && !line.hidden && !line.widgetFunction) updateLineHeight(line, 1); if (line.text.length > maxLine.length) maxLine = line.text; }); } @@ -1569,8 +1570,8 @@ var CodeMirror = (function() { var tempId = Math.floor(Math.random() * 0xffffff).toString(16); function measureLine(line, ch) { if (ch == 0) return {top: 0, left: 0}; - if (line.isWidgetBlock) { - var size = line.styles[1].size(line.text); + if (line.widgetFunction) { + var size = line.widgetFunction.size(line.text); return {top: -1, left: size.width}; } var extra = ""; @@ -2288,7 +2289,7 @@ var CodeMirror = (function() { this.height = 1; this.marked = this.gutterMarker = this.className = this.bgClassName = this.handlers = null; this.stateAfter = this.parent = this.hidden = null; - this.isWidgetBlock = false; + this.widgetFunction = null; } Line.inheritMarks = function(text, orig) { var ln = new Line(text), mk = orig && orig.marked; @@ -2421,14 +2422,19 @@ var CodeMirror = (function() { } if (st.length != pos) {st.length = pos; changed = true;} if (pos && st[pos-2] != prevWord) changed = true; - this.isWidgetBlock = (st.length == 2 && st[1] && typeof st[1] == 'object'); + if (st.length == 2 && typeof st[1] == 'object') { + this.widgetFunction = st[1]; + st[1] = null; + } else { + this.widgetFunction = null; + } // Short lines with simple highlights return null, and are // counted as changed by the driver because they are likely to // highlight the same way in various contexts. return changed || (st.length < 5 && this.text.length < 10 ? null : false); }, nodeAdded: function(node) { - if (this.isWidgetBlock) this.styles[1].callback(node, this); + if (this.widgetFunction) this.widgetFunction.callback(node, this); //this.setHeight(node.clientHeight); }, // Fetch the parser token for a given character. Useful for hacks @@ -2480,7 +2486,7 @@ var CodeMirror = (function() { } var st = this.styles, allText = this.text, marked = this.marked; var len = allText.length; - if (this.isWidgetBlock) return st[1].creator(allText); + if (this.widgetFunction) return this.widgetFunction.creator(allText); if (endAt != null) len = Math.min(endAt, len); function styleToClass(style) { if (!style) return null; diff --git a/static/js/CodeMirror/mitx_markdown.js b/static/js/CodeMirror/mitx_markdown.js index aa63967c58..9b95d96d19 100644 --- a/static/js/CodeMirror/mitx_markdown.js +++ b/static/js/CodeMirror/mitx_markdown.js @@ -35,7 +35,7 @@ $(function(){ editingCircuit.schematic.load_schematic(saving_circuit, ""); if (editingCircuit.codeMirrorLine) { - editingCircuit.codeMirrorLine.text = "circuit-schematic:" + saving_circuit; + editingCircuit.codeMirrorLine.replace(0, null, "circuit-schematic:" + saving_circuit); } $(".modal_close").first().click(); From 3c015bb0a9716ac6a3da7c9d98ae54e374ada96a Mon Sep 17 00:00:00 2001 From: Bridger Maxwell Date: Wed, 28 Mar 2012 00:56:44 -0700 Subject: [PATCH 08/50] Another codemirror fix. --- static/js/CodeMirror/codemirror.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/static/js/CodeMirror/codemirror.js b/static/js/CodeMirror/codemirror.js index 261070d6af..f517176c46 100644 --- a/static/js/CodeMirror/codemirror.js +++ b/static/js/CodeMirror/codemirror.js @@ -753,6 +753,8 @@ var CodeMirror = (function() { if (line.widgetFunction) { var guess = line.widgetFunction.size(line.text).height / textHeight(); if (guess != line.height) updateLineHeight(line, guess); + } else if (line.height != 1) { + updateLineHeight(line, 1); } }); if (recomputeMaxLength) { From 99b8a5110163de995d09cc0b8ae900f574f15824 Mon Sep 17 00:00:00 2001 From: Bridger Maxwell Date: Wed, 28 Mar 2012 01:00:33 -0700 Subject: [PATCH 09/50] Removed debug comments. --- static/js/CodeMirror/codemirror.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/static/js/CodeMirror/codemirror.js b/static/js/CodeMirror/codemirror.js index f517176c46..ed92bc8378 100644 --- a/static/js/CodeMirror/codemirror.js +++ b/static/js/CodeMirror/codemirror.js @@ -498,17 +498,17 @@ var CodeMirror = (function() { if (!bound) return false; } var prevShift = shiftSelecting; - // try { //TODO: Reenable this try/catch + try { if (options.readOnly) suppressEdits = true; if (dropShift) shiftSelecting = null; bound(instance); - // } catch(e) { - // if (e != Pass) throw e; - // return false; - // } finally { - // shiftSelecting = prevShift; - // suppressEdits = false; - // } + } catch(e) { + if (e != Pass) throw e; + return false; + } finally { + shiftSelecting = prevShift; + suppressEdits = false; + } return true; } function handleKeyBinding(e) { From f95aa496facf7bb4266621f38bd6e5a934edb108 Mon Sep 17 00:00:00 2001 From: Bridger Maxwell Date: Wed, 28 Mar 2012 14:09:21 -0700 Subject: [PATCH 10/50] Better compatibility with the Codemirror editor and the markdown circuit extension. --- djangoapps/simplewiki/mdx_circuit.py | 40 ++++++++++++++++++++------- djangoapps/simplewiki/models.py | 1 - static/js/CodeMirror/codemirror.js | 2 +- static/js/CodeMirror/mitx_markdown.js | 2 ++ templates/simplewiki_edit.html | 2 +- 5 files changed, 34 insertions(+), 13 deletions(-) diff --git a/djangoapps/simplewiki/mdx_circuit.py b/djangoapps/simplewiki/mdx_circuit.py index 33377eb019..72e432012f 100755 --- a/djangoapps/simplewiki/mdx_circuit.py +++ b/djangoapps/simplewiki/mdx_circuit.py @@ -5,12 +5,14 @@ Image Circuit Extension for Python-Markdown circuit:name becomes the circuit. ''' +import markdown +import re import simplewiki.settings as settings from mitxmako.shortcuts import render_to_response, render_to_string -import markdown + try: # Markdown 2.1.0 changed from 2.0.3. We try importing the new version first, # but import the 2.0.3 version if it fails @@ -22,22 +24,40 @@ class CircuitExtension(markdown.Extension): def __init__(self, configs): for key, value in configs : self.setConfig(key, value) - - def add_inline(self, md, name, klass, re): - pattern = klass(re) - pattern.md = md - pattern.ext = self - md.inlinePatterns.add(name, pattern, ".*)$') + ## Because Markdown treats contigous lines as one block of text, it is hard to match + ## a regex that must occupy the whole line (like the circuit regex). This is why we have + ## a preprocessor that inspects the lines and replaces the matched lines with text that is + ## easier to match + md.preprocessors.add('circuit', CircuitPreprocessor(md), "_begin") + + pattern = CircuitLink(r'processed-schematic:(?P.*?)processed-schematic-end') + pattern.md = md + pattern.ext = self + md.inlinePatterns.add('circuit', pattern, ".*)$') + + def run(self, lines): + new_lines = [] + for line in lines: + m = self.preRegex.match(line) + if m: + new_lines.append('processed-schematic:{0}processed-schematic-end'.format( m.group('data') )) + else: + new_lines.append(line) + return new_lines + class CircuitLink(markdown.inlinepatterns.Pattern): def handleMatch(self, m): data = m.group('data') - ##TODO: We need to html escape the data - return etree.fromstring("") + return etree.fromstring("") def makeExtension(configs=None) : diff --git a/djangoapps/simplewiki/models.py b/djangoapps/simplewiki/models.py index 33c9b0403e..71a57c601c 100644 --- a/djangoapps/simplewiki/models.py +++ b/djangoapps/simplewiki/models.py @@ -276,7 +276,6 @@ class Revision(models.Model): # Create pre-parsed contents - no need to parse on-the-fly ext = WIKI_MARKDOWN_EXTENSIONS ext += ["wikipath(base_url=%s)" % reverse('wiki_view', args=('/',))] - print ext self.contents_parsed = markdown(self.contents, extensions=ext, safe_mode='escape',) diff --git a/static/js/CodeMirror/codemirror.js b/static/js/CodeMirror/codemirror.js index ed92bc8378..c840de05b7 100644 --- a/static/js/CodeMirror/codemirror.js +++ b/static/js/CodeMirror/codemirror.js @@ -980,7 +980,7 @@ var CodeMirror = (function() { maxWidth = scroller.clientWidth; var curNode = lineDiv.firstChild, heightChanged = false; doc.iter(showingFrom, showingTo, function(line) { - if (!line.hidden && !line.widgetFunction) { //TODO: We should handle widget blocks better here + if (!line.hidden && !line.widgetFunction) { var height = Math.round(curNode.offsetHeight / th) || 1; if (line.widgetFunction) height = line.widgetFunction.size(line.text).height / textHeight(); if (line.height != height) { diff --git a/static/js/CodeMirror/mitx_markdown.js b/static/js/CodeMirror/mitx_markdown.js index 9b95d96d19..9991a4f05c 100644 --- a/static/js/CodeMirror/mitx_markdown.js +++ b/static/js/CodeMirror/mitx_markdown.js @@ -77,6 +77,8 @@ CodeMirror.defineMode("mitx_markdown", function(cmCfg, modeCfg) { update_schematics(); var schmInput = node.firstChild; schmInput.codeMirrorLine = line; + schmInput.schematic.always_draw_grid = true; + schmInput.schematic.redraw_background(); $(node).leanModal(); } }; diff --git a/templates/simplewiki_edit.html b/templates/simplewiki_edit.html index 75e1fec325..85059a1766 100644 --- a/templates/simplewiki_edit.html +++ b/templates/simplewiki_edit.html @@ -39,7 +39,7 @@ mode: 'mitx_markdown', matchBrackets: true, theme: "default", - lineWrapping: false, + lineWrapping: true, }); //Store the inital contents so we can compare for unsaved changes From bb15d923813345fc487ccea29e6a633974f02768 Mon Sep 17 00:00:00 2001 From: Bridger Maxwell Date: Wed, 28 Mar 2012 14:20:08 -0700 Subject: [PATCH 11/50] Fixed leanModal bug that caused lots of overlays to be added. --- static/js/jquery.leanModal.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/static/js/jquery.leanModal.js b/static/js/jquery.leanModal.js index 3d2dd16bb9..dce1b51804 100644 --- a/static/js/jquery.leanModal.js +++ b/static/js/jquery.leanModal.js @@ -10,9 +10,12 @@ closeButton:'.modal_close' } - var overlay = $("
"); + var overlay = $("#lean_overlay"); + if (overlay.length == 0) { + overlay = $("
"); + $("body").append(overlay); + } - $("body").append(overlay); options = $.extend(defaults, options); From f62a43965b1875d48da7fb48dde70dc62fa2530a Mon Sep 17 00:00:00 2001 From: Bridger Maxwell Date: Wed, 28 Mar 2012 22:50:23 -0700 Subject: [PATCH 12/50] Fixed bug for selecting lines with non-integer heights. It put up a fight... --- static/js/CodeMirror/codemirror.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/static/js/CodeMirror/codemirror.js b/static/js/CodeMirror/codemirror.js index c840de05b7..b20a7d033b 100644 --- a/static/js/CodeMirror/codemirror.js +++ b/static/js/CodeMirror/codemirror.js @@ -1613,7 +1613,7 @@ var CodeMirror = (function() { var lineNo = lineAtHeight(doc, heightPos); if (lineNo >= doc.size) return {line: doc.size - 1, ch: getLine(doc.size - 1).text.length}; var lineObj = getLine(lineNo), text = lineObj.text; - var tw = options.lineWrapping, innerOff = tw ? heightPos - heightAtLine(doc, lineNo) : 0; + var tw = options.lineWrapping, innerOff = tw ? Math.floor(heightPos - heightAtLine(doc, lineNo)) : 0; if (x <= 0 && innerOff == 0) return {line: lineNo, ch: 0}; function getX(len) { var sp = measureLine(lineObj, len); @@ -2437,7 +2437,6 @@ var CodeMirror = (function() { }, nodeAdded: function(node) { if (this.widgetFunction) this.widgetFunction.callback(node, this); - //this.setHeight(node.clientHeight); }, // Fetch the parser token for a given character. Useful for hacks // that want to inspect the mode state (say, for completion). From b2fbf537e4f90d06b9aa7932dd683cad9ac570b2 Mon Sep 17 00:00:00 2001 From: Bridger Maxwell Date: Thu, 29 Mar 2012 00:16:03 -0700 Subject: [PATCH 13/50] Properly escaping the schematic html attributes. --- djangoapps/simplewiki/mdx_circuit.py | 4 +++- static/js/CodeMirror/mitx_markdown.js | 16 ++++++++++++---- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/djangoapps/simplewiki/mdx_circuit.py b/djangoapps/simplewiki/mdx_circuit.py index 72e432012f..b598e184c6 100755 --- a/djangoapps/simplewiki/mdx_circuit.py +++ b/djangoapps/simplewiki/mdx_circuit.py @@ -10,6 +10,7 @@ import re import simplewiki.settings as settings +from django.utils.html import escape from mitxmako.shortcuts import render_to_response, render_to_string @@ -56,8 +57,9 @@ class CircuitPreprocessor(markdown.preprocessors.Preprocessor): class CircuitLink(markdown.inlinepatterns.Pattern): def handleMatch(self, m): data = m.group('data') + data = escape(data) ##TODO: We need to html escape the data - return etree.fromstring("") + return etree.fromstring("
") def makeExtension(configs=None) : diff --git a/static/js/CodeMirror/mitx_markdown.js b/static/js/CodeMirror/mitx_markdown.js index 9991a4f05c..9674d52264 100644 --- a/static/js/CodeMirror/mitx_markdown.js +++ b/static/js/CodeMirror/mitx_markdown.js @@ -58,20 +58,28 @@ CodeMirror.defineMode("mitx_markdown", function(cmCfg, modeCfg) { , strong = 'strong' , emstrong = 'emstrong'; + function escapeHtml(unsafe) { + return unsafe + .replace(/&/g, "&") + .replace(//g, ">") + .replace(/"/g, """) + .replace(/'/g, "'"); + } + var circuit_formatter = { creator: function(text) { var circuit_value = text.match(circuitRE)[1] - //TODO: We need real html escaping here - circuit_value = CodeMirror.htmlEscape(circuit_value);// circuit_value.replace("\"", "'"); + circuit_value = escapeHtml(circuit_value); var html = "" + - ""; + ""; return html; }, size: function(text) { - return {width: 150, height:154}; + return {width: 150, height:152}; }, callback: function(node, line) { update_schematics(); From d51341301ddf2d92b565c2ba094774c20593bea9 Mon Sep 17 00:00:00 2001 From: Bridger Maxwell Date: Thu, 29 Mar 2012 15:56:21 -0700 Subject: [PATCH 14/50] Changed some css properties for proper sizing of elements. --- static/js/CodeMirror/mitx_markdown.js | 56 ++++++++++++++++----------- 1 file changed, 33 insertions(+), 23 deletions(-) diff --git a/static/js/CodeMirror/mitx_markdown.js b/static/js/CodeMirror/mitx_markdown.js index 9674d52264..6c0cb9dc26 100644 --- a/static/js/CodeMirror/mitx_markdown.js +++ b/static/js/CodeMirror/mitx_markdown.js @@ -1,8 +1,13 @@ +var schematic_height = 153; +var schematic_width = 400; + $(function(){ $(document).ready(function() { $("a[rel*=leanModal]").leanModal(); - $("body").append('
'); + $("body").append('
'+ + '' + + '
'); //This is the editor that pops up as a modal var editorCircuit = $("#schematic_editor").get(0); @@ -43,6 +48,7 @@ $(function(){ }); }); + CodeMirror.defineMode("mitx_markdown", function(cmCfg, modeCfg) { var htmlMode = CodeMirror.getMode(cmCfg, { name: 'xml', htmlMode: true }); @@ -66,30 +72,34 @@ CodeMirror.defineMode("mitx_markdown", function(cmCfg, modeCfg) { .replace(/"/g, """) .replace(/'/g, "'"); } - - var circuit_formatter = { - creator: function(text) { - var circuit_value = text.match(circuitRE)[1] + + var circuit_formatter = { + creator: function(text) { + var circuit_value = text.match(circuitRE)[1] - circuit_value = escapeHtml(circuit_value); + circuit_value = escapeHtml(circuit_value); - var html = "" + - ""; - - return html; - }, - size: function(text) { - return {width: 150, height:152}; - }, - callback: function(node, line) { - update_schematics(); - var schmInput = node.firstChild; - schmInput.codeMirrorLine = line; - schmInput.schematic.always_draw_grid = true; - schmInput.schematic.redraw_background(); - $(node).leanModal(); - } - }; + var html = ""; + + return html; + }, + size: function(text) { + return {width: schematic_width, height:schematic_height}; + }, + callback: function(node, line) { + update_schematics(); + var schmInput = node.firstChild.firstChild; + schmInput.codeMirrorLine = line; + if (schmInput.schematic) { //This is undefined if there was an error making the schematic + schmInput.schematic.canvas.style.display = "block"; //Otherwise, it gets line height and is a weird size + schmInput.schematic.always_draw_grid = true; + schmInput.schematic.redraw_background(); + } + $(node.firstChild).leanModal(); + } + }; + var hrRE = /^[*-=_]/ , ulRE = /^[*-+]\s+/ From 4938fb63dac39be0bd3c912d57dfdfcde75b2169 Mon Sep 17 00:00:00 2001 From: Bridger Maxwell Date: Thu, 29 Mar 2012 16:39:06 -0700 Subject: [PATCH 15/50] Set size of wiki circuits --- djangoapps/simplewiki/mdx_circuit.py | 2 +- static/js/CodeMirror/mitx_markdown.js | 4 ++-- templates/simplewiki_edit.html | 9 ++++----- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/djangoapps/simplewiki/mdx_circuit.py b/djangoapps/simplewiki/mdx_circuit.py index b598e184c6..ed1298e118 100755 --- a/djangoapps/simplewiki/mdx_circuit.py +++ b/djangoapps/simplewiki/mdx_circuit.py @@ -59,7 +59,7 @@ class CircuitLink(markdown.inlinepatterns.Pattern): data = m.group('data') data = escape(data) ##TODO: We need to html escape the data - return etree.fromstring("
") + return etree.fromstring("
") def makeExtension(configs=None) : diff --git a/static/js/CodeMirror/mitx_markdown.js b/static/js/CodeMirror/mitx_markdown.js index 6c0cb9dc26..a6f5ed14c6 100644 --- a/static/js/CodeMirror/mitx_markdown.js +++ b/static/js/CodeMirror/mitx_markdown.js @@ -1,5 +1,5 @@ -var schematic_height = 153; -var schematic_width = 400; +var schematic_height = 300; +var schematic_width = 500; $(function(){ $(document).ready(function() { diff --git a/templates/simplewiki_edit.html b/templates/simplewiki_edit.html index 85059a1766..87e53869fa 100644 --- a/templates/simplewiki_edit.html +++ b/templates/simplewiki_edit.html @@ -19,14 +19,13 @@ From 283ad48c25b4d16518de994fba0086da75edea0b Mon Sep 17 00:00:00 2001 From: Bridger Maxwell Date: Thu, 29 Mar 2012 16:41:44 -0700 Subject: [PATCH 16/50] Changed instructions for circuit-schematic: --- templates/simplewiki_instructions.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/simplewiki_instructions.html b/templates/simplewiki_instructions.html index 8b579feda2..58ef12a47c 100644 --- a/templates/simplewiki_instructions.html +++ b/templates/simplewiki_instructions.html @@ -2,7 +2,7 @@ This wiki uses Markdown for styling. There are several useful guides online.

MITx Additions: -

circuit:basic

+

circuit-schematic:

$LaTeX Math Expression$

To create a new wiki article, create a link to it. Clicking the link gives you the creation page.

[Article Name](wiki:ArticleName)

From d283bb57940e0e6ae8189eef67cd6d34d740471f Mon Sep 17 00:00:00 2001 From: Bridger Maxwell Date: Thu, 29 Mar 2012 18:00:42 -0700 Subject: [PATCH 17/50] Moved bug fix to minified leanModal --- static/js/jquery.leanModal.min.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/static/js/jquery.leanModal.min.js b/static/js/jquery.leanModal.min.js index e50d4edfd5..2ea21ad3df 100644 --- a/static/js/jquery.leanModal.min.js +++ b/static/js/jquery.leanModal.min.js @@ -1 +1,2 @@ -(function(a){a.fn.extend({leanModal:function(b){function e(b){a("#lean_overlay").fadeOut(200);a(b).css({display:"none"})}var c={top:100,overlay:.5,closeButton:".modal_close"};var d=a("
");a("body").append(d);b=a.extend(c,b);return this.each(function(){var c=b;a(this).click(function(b){var f=a(this).attr("href");a(".leanModal_box").css({display:"none"});a("body").append(d);a(".leanModal_box").append('');a("#lean_overlay").click(function(){e(f)});a(c.closeButton).click(function(){e(f)});var g=a(f).outerHeight();var h=a(f).outerWidth();a("#lean_overlay").css({display:"block",opacity:0});a("#lean_overlay").fadeTo(200,c.overlay);a(f).css({display:"block",position:"fixed",opacity:0,"z-index":11e3,left:50+"%","margin-left":-(h/2)+"px",top:c.top+"px"});var i=a(f).offset().top+"px";a(f).css({position:"absolute",top:i});a(f).fadeTo(200,1);b.preventDefault()})})}})})(jQuery) +(function(a){a.fn.extend({leanModal:function(c){function g(d){a("#lean_overlay").fadeOut(200);a(d).css({display:"none"})}var e=a("#lean_overlay");0==e.length&&(e=a("
"),a("body").append(e));c=a.extend({top:100,overlay:0.5,closeButton:".modal_close"},c);return this.each(function(){var d=c;a(this).click(function(c){var b=a(this).attr("href");a(".leanModal_box").css({display:"none"});a("body").append(e);a(".leanModal_box").append(''); +a("#lean_overlay").click(function(){g(b)});a(d.closeButton).click(function(){g(b)});a(b).outerHeight();var f=a(b).outerWidth();a("#lean_overlay").css({display:"block",opacity:0});a("#lean_overlay").fadeTo(200,d.overlay);a(b).css({display:"block",position:"fixed",opacity:0,"z-index":11E3,left:"50%","margin-left":-(f/2)+"px",top:d.top+"px"});f=a(b).offset().top+"px";a(b).css({position:"absolute",top:f});a(b).fadeTo(200,1);c.preventDefault()})})}})})(jQuery); \ No newline at end of file From d7383bf3c4b4eb35b84180998f656399e9b91f3e Mon Sep 17 00:00:00 2001 From: Bridger Maxwell Date: Thu, 29 Mar 2012 18:21:25 -0700 Subject: [PATCH 18/50] Small cleanups in codemirror. --- static/js/CodeMirror/codemirror.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/static/js/CodeMirror/codemirror.js b/static/js/CodeMirror/codemirror.js index b20a7d033b..35d6c25103 100644 --- a/static/js/CodeMirror/codemirror.js +++ b/static/js/CodeMirror/codemirror.js @@ -617,6 +617,7 @@ var CodeMirror = (function() { function updateLines(from, to, newText, selFrom, selTo) { if (suppressEdits) return; + //This block ensures that widget lines don't have any text inserted on the same line. if (from.ch > 0 && (newText[0] != '' || newText.length == 1) && getLine(from.line).widgetFunction) { newText.unshift(''); var widgetLine = getLine(from.line); @@ -980,7 +981,7 @@ var CodeMirror = (function() { maxWidth = scroller.clientWidth; var curNode = lineDiv.firstChild, heightChanged = false; doc.iter(showingFrom, showingTo, function(line) { - if (!line.hidden && !line.widgetFunction) { + if (!line.hidden) { var height = Math.round(curNode.offsetHeight / th) || 1; if (line.widgetFunction) height = line.widgetFunction.size(line.text).height / textHeight(); if (line.height != height) { @@ -1053,7 +1054,6 @@ var CodeMirror = (function() { // This pass fills in the lines that actually changed. var nextIntact = intact.shift(), curNode = lineDiv.firstChild, j = from; var scratch = document.createElement("div"); - var text_height = textHeight(); //Remove this once heights are in pixels instead of lines doc.iter(from, to, function(line) { if (nextIntact && nextIntact.to == j) nextIntact = intact.shift(); if (!nextIntact || nextIntact.from > j) { @@ -2143,7 +2143,7 @@ var CodeMirror = (function() { }; return instance; }; - + // Utility functions for working with state. Exported because modes // sometimes need to do this. function copyState(mode, state) { @@ -2493,6 +2493,7 @@ var CodeMirror = (function() { if (!style) return null; return "cm-" + style.replace(/ +/g, " cm-"); } + if (!allText && endAt == null) span(" "); else if (!marked || !marked.length) From 183774f6376fb1e545138dd699617ad11f651b28 Mon Sep 17 00:00:00 2001 From: Bridger Maxwell Date: Thu, 29 Mar 2012 22:27:55 -0700 Subject: [PATCH 19/50] Fixed codemirror arrowkey navigation bug. --- static/js/CodeMirror/codemirror.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/static/js/CodeMirror/codemirror.js b/static/js/CodeMirror/codemirror.js index 35d6c25103..ff127f80f6 100644 --- a/static/js/CodeMirror/codemirror.js +++ b/static/js/CodeMirror/codemirror.js @@ -1295,9 +1295,9 @@ var CodeMirror = (function() { if (goalColumn != null) pos.x = goalColumn; if (unit == "page") dist = Math.min(scroller.clientHeight, window.innerHeight || document.documentElement.clientHeight); else if (unit == "line") { - if (dir > 0) { - dist = Math.ceil(getLine(pos.line).height); - } else dist = 1; + var line = getLine(pos.line); + if (dir > 0 && line.widgetFunction) dist = Math.ceil(getLine(pos.line).height); + else dist = 1; dist *= textHeight(); } var target = coordsChar(loc.x, loc.y + dist * dir + 2); From acbe1e886071d166cffdbc0ec7dfc2cbba97f5dc Mon Sep 17 00:00:00 2001 From: Bridger Maxwell Date: Fri, 30 Mar 2012 05:49:32 -0700 Subject: [PATCH 20/50] Re-enabled unsaved changes confirmation on wiki edit. --- templates/simplewiki_edit.html | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/templates/simplewiki_edit.html b/templates/simplewiki_edit.html index 87e53869fa..d5bd22cf8a 100644 --- a/templates/simplewiki_edit.html +++ b/templates/simplewiki_edit.html @@ -44,12 +44,12 @@ //Store the inital contents so we can compare for unsaved changes var initial_contents = editor.getValue(); - // window.onbeforeunload = function askConfirm() { //Warn the user before they navigate away - // if ( editor.getValue() != initial_contents ) { - // return "You have made changes to the article that have not been saved yet."; - // } - // }; - + window.onbeforeunload = function askConfirm() { //Warn the user before they navigate away + if ( editor.getValue() != initial_contents ) { + return "You have made changes to the article that have not been saved yet."; + } + }; + $("#submit_edit").click(function() { initial_contents = editor.getValue(); }); From 6f2f218bead79908a324697664af1d0ba23f5e0d Mon Sep 17 00:00:00 2001 From: Bridger Maxwell Date: Fri, 30 Mar 2012 06:03:10 -0700 Subject: [PATCH 21/50] The wiki creation page now uses the edit template, so it has the unsaved changes confirmation and Codemirror editor. --- djangoapps/simplewiki/views.py | 4 +++- templates/simplewiki_create.html | 24 ------------------------ templates/simplewiki_edit.html | 20 ++++++++++++++++---- 3 files changed, 19 insertions(+), 29 deletions(-) delete mode 100644 templates/simplewiki_create.html diff --git a/djangoapps/simplewiki/views.py b/djangoapps/simplewiki/views.py index 7b30b74907..7d743139ba 100644 --- a/djangoapps/simplewiki/views.py +++ b/djangoapps/simplewiki/views.py @@ -154,10 +154,11 @@ def create(request, wiki_url): d = {'wiki_form': f, 'wiki_write': True, + 'create_article' : True, } d.update(csrf(request)) - return render_to_response('simplewiki_create.html', d) + return render_to_response('simplewiki_edit.html', d) def edit(request, wiki_url): if not request.user.is_authenticated(): @@ -207,6 +208,7 @@ def edit(request, wiki_url): 'wiki_article': article, 'wiki_title' : article.title, 'wiki_attachments_write': article.can_attach(request.user), + 'create_article' : False, } d.update(csrf(request)) diff --git a/templates/simplewiki_create.html b/templates/simplewiki_create.html deleted file mode 100644 index 42b6bdc2cb..0000000000 --- a/templates/simplewiki_create.html +++ /dev/null @@ -1,24 +0,0 @@ -##This file is based on the template from the SimpleWiki source which carries the GPL license - -<%inherit file="simplewiki_base.html"/> - -<%block name="title">Wiki – Create Article – MITx 6.002x - -<%block name="wiki_page_title"> -

Create article

- - -<%block name="wiki_body"> -
-
- -
- - ${ wiki_form } - - -
- -<%include file="simplewiki_instructions.html"/> - - diff --git a/templates/simplewiki_edit.html b/templates/simplewiki_edit.html index d5bd22cf8a..11f275bfbc 100644 --- a/templates/simplewiki_edit.html +++ b/templates/simplewiki_edit.html @@ -2,22 +2,30 @@ <%inherit file="simplewiki_base.html"/> -<%block name="title">${"Edit " + wiki_title + " - " if wiki_title is not UNDEFINED else ""}MITx 6.002x Wiki +<%block name="title"> + +%if create_article: +Wiki – Create Article – MITx 6.002x +%else: +${"Edit " + wiki_title + " - " if wiki_title is not UNDEFINED else ""}MITx 6.002x Wiki +%endif + <%block name="wiki_page_title"> +%if create_article: +

Create article

+%else:

${ wiki_article.title }

+%endif <%block name="wiki_head"> - - - - - diff --git a/templates/video_init.js b/templates/video_init.js index dc94763be3..787a9be5ad 100644 --- a/templates/video_init.js +++ b/templates/video_init.js @@ -37,7 +37,7 @@ function add_speed(key, stream) { var id = 'speed_' + stream; if (key == video_speed) { - $("#video_speeds").append('
  • '+key+'x
  • '); + $("#video_speeds").append('
  • '+key+'x
  • '); } else { $("#video_speeds").append('
  • '+key+'x
  • '); } From 1016843ae2559d321d707d70e7d0849cea473b23 Mon Sep 17 00:00:00 2001 From: Kyle Fiedler Date: Tue, 3 Apr 2012 11:46:45 -0400 Subject: [PATCH 33/50] Added style and js for speed picker and hover for scrubber --- static/css/application.css | 133 +++++++++++++++++++++----- templates/sass/courseware/_video.scss | 69 ++++++++++--- templates/video.html | 9 +- templates/video_init.js | 3 + 4 files changed, 176 insertions(+), 38 deletions(-) diff --git a/static/css/application.css b/static/css/application.css index 53237c27f5..ee4fa0cc06 100644 --- a/static/css/application.css +++ b/static/css/application.css @@ -2830,26 +2830,84 @@ section.course-content div.video-subtitles div.video-wrapper section.video-contr background: #c2c2c2; border: none; border-bottom: 1px solid #000; - height: 14px; } + height: 7px; + -webkit-transition-property: all; + -moz-transition-property: all; + -ms-transition-property: all; + -o-transition-property: all; + transition-property: all; + -webkit-transition-duration: 0.15s; + -moz-transition-duration: 0.15s; + -ms-transition-duration: 0.15s; + -o-transition-duration: 0.15s; + transition-duration: 0.15s; + -webkit-transition-timing-function: ease-out; + -moz-transition-timing-function: ease-out; + -ms-transition-timing-function: ease-out; + -o-transition-timing-function: ease-out; + transition-timing-function: ease-out; + -webkit-transition-delay: 0; + -moz-transition-delay: 0; + -ms-transition-delay: 0; + -o-transition-delay: 0; + transition-delay: 0; } section.course-content div.video-subtitles div.video-wrapper section.video-controls div#slider a.ui-slider-handle { - -webkit-border-radius: 20px; - -moz-border-radius: 20px; - -ms-border-radius: 20px; - -o-border-radius: 20px; - border-radius: 20px; + -webkit-border-radius: 15px; + -moz-border-radius: 15px; + -ms-border-radius: 15px; + -o-border-radius: 15px; + border-radius: 15px; -webkit-box-shadow: inset 0 1px 0 #bf4040; -moz-box-shadow: inset 0 1px 0 #bf4040; box-shadow: inset 0 1px 0 #bf4040; background: #993333 url(/static/images/slider-handle.png) center center no-repeat; border: 1px solid #4d1919; cursor: pointer; + height: 15px; + margin-left: -7px; + top: -4px; + width: 15px; + -webkit-transition-property: all; + -moz-transition-property: all; + -ms-transition-property: all; + -o-transition-property: all; + transition-property: all; + -webkit-transition-duration: 0.15s; + -moz-transition-duration: 0.15s; + -ms-transition-duration: 0.15s; + -o-transition-duration: 0.15s; + transition-duration: 0.15s; + -webkit-transition-timing-function: ease-out; + -moz-transition-timing-function: ease-out; + -ms-transition-timing-function: ease-out; + -o-transition-timing-function: ease-out; + transition-timing-function: ease-out; + -webkit-transition-delay: 0; + -moz-transition-delay: 0; + -ms-transition-delay: 0; + -o-transition-delay: 0; + transition-delay: 0; + -webkit-background-size: 50%; + -moz-background-size: 50%; + -ms-background-size: 50%; + -o-background-size: 50%; + background-size: 50%; } +section.course-content div.video-subtitles div.video-wrapper section.video-controls div#slider a.ui-slider-handle:focus, section.course-content div.video-subtitles div.video-wrapper section.video-controls div#slider a.ui-slider-handle:hover { + background-color: #bf4040; + outline: none; } +section.course-content div.video-subtitles div.video-wrapper section.video-controls div#slider:hover { + height: 14px; + margin-top: -7px; } +section.course-content div.video-subtitles div.video-wrapper section.video-controls div#slider:hover a.ui-slider-handle { + -webkit-border-radius: 20px; + -moz-border-radius: 20px; + -ms-border-radius: 20px; + -o-border-radius: 20px; + border-radius: 20px; height: 20px; margin-left: -10px; top: -4px; width: 20px; } -section.course-content div.video-subtitles div.video-wrapper section.video-controls div#slider a.ui-slider-handle:focus, section.course-content div.video-subtitles div.video-wrapper section.video-controls div#slider a.ui-slider-handle:hover { - background-color: #bf4040; - outline: none; } section.course-content div.video-subtitles div.video-wrapper section.video-controls ul.vcr { float: left; margin-right: 22.652px; } @@ -2884,16 +2942,15 @@ section.course-content div.video-subtitles div.video-wrapper section.video-contr section.course-content div.video-subtitles div.video-wrapper section.video-controls div.secondary-controls { float: right; } section.course-content div.video-subtitles div.video-wrapper section.video-controls div.secondary-controls div.speeds { - border-right: 1px solid #000; border-left: 1px solid #000; + border-right: 1px solid #000; -webkit-box-shadow: 1px 0 0 #555555, inset 1px 0 0 #555555; -moz-box-shadow: 1px 0 0 #555555, inset 1px 0 0 #555555; box-shadow: 1px 0 0 #555555, inset 1px 0 0 #555555; float: left; line-height: 46px; margin-right: 0; - -webkit-font-smoothing: antialiased; - opacity: .7; + position: relative; -webkit-transition-property: all; -moz-transition-property: all; -ms-transition-property: all; @@ -2913,7 +2970,9 @@ section.course-content div.video-subtitles div.video-wrapper section.video-contr -moz-transition-delay: 0; -ms-transition-delay: 0; -o-transition-delay: 0; - transition-delay: 0; } + transition-delay: 0; + width: 110px; + -webkit-font-smoothing: antialiased; } section.course-content div.video-subtitles div.video-wrapper section.video-controls div.secondary-controls div.speeds h3, section.course-content div.video-subtitles div.video-wrapper section.video-controls div.secondary-controls div.speeds div#wiki_panel input[type="button"], div#wiki_panel section.course-content div.video-subtitles div.video-wrapper section.video-controls div.secondary-controls div.speeds input[type="button"] { display: -moz-inline-box; -moz-box-orient: vertical; @@ -2922,9 +2981,13 @@ section.course-content div.video-subtitles div.video-wrapper section.video-contr zoom: 1; *display: inline; *vertical-align: auto; - padding: 0 11.326px; - font-weight: normal; } -section.course-content div.video-subtitles div.video-wrapper section.video-controls div.secondary-controls div.speeds ol#video_speeds { + padding: 0 5.663px 0 11.326px; + font-weight: normal; + text-transform: uppercase; + font-size: 12px; + letter-spacing: 1px; + color: #999; } +section.course-content div.video-subtitles div.video-wrapper section.video-controls div.secondary-controls div.speeds p.active { display: -moz-inline-box; -moz-box-orient: vertical; display: inline-block; @@ -2932,21 +2995,47 @@ section.course-content div.video-subtitles div.video-wrapper section.video-contr zoom: 1; *display: inline; *vertical-align: auto; - padding-right: 11.326px; } -section.course-content div.video-subtitles div.video-wrapper section.video-controls div.secondary-controls div.speeds ol#video_speeds li { - cursor: pointer; - color: #fff; + padding: 0 11.326px 0 0; + margin-bottom: 0; + font-weight: bold; } +section.course-content div.video-subtitles div.video-wrapper section.video-controls div.secondary-controls div.speeds ol#video_speeds { + background-color: #444; + border: 1px solid #000; + border-top: 0; + -webkit-box-shadow: inset 1px 0 0 #555555; + -moz-box-shadow: inset 1px 0 0 #555555; + box-shadow: inset 1px 0 0 #555555; display: -moz-inline-box; -moz-box-orient: vertical; display: inline-block; vertical-align: baseline; zoom: 1; *display: inline; - *vertical-align: auto; } + *vertical-align: auto; + left: -1px; + position: absolute; + top: 48px; + width: 100%; + z-index: 10; } +section.course-content div.video-subtitles div.video-wrapper section.video-controls div.secondary-controls div.speeds ol#video_speeds li { + border-bottom: 1px solid #000; + -webkit-box-shadow: 0 1px 0 #555555; + -moz-box-shadow: 0 1px 0 #555555; + box-shadow: 0 1px 0 #555555; + color: #fff; + cursor: pointer; + padding: 0 11.326px; } section.course-content div.video-subtitles div.video-wrapper section.video-controls div.secondary-controls div.speeds ol#video_speeds li.active { font-weight: bold; } +section.course-content div.video-subtitles div.video-wrapper section.video-controls div.secondary-controls div.speeds ol#video_speeds li:last-child { + border-bottom: 0; + margin-top: 0; + -webkit-box-shadow: none; + -moz-box-shadow: none; + box-shadow: none; } section.course-content div.video-subtitles div.video-wrapper section.video-controls div.secondary-controls div.speeds ol#video_speeds li:hover { - color: #aaa; } + color: #aaa; + background-color: #666; } section.course-content div.video-subtitles div.video-wrapper section.video-controls div.secondary-controls div.speeds:hover { opacity: 1; background-color: #444; } diff --git a/templates/sass/courseware/_video.scss b/templates/sass/courseware/_video.scss index 5f83f11c39..1ec241dda1 100644 --- a/templates/sass/courseware/_video.scss +++ b/templates/sass/courseware/_video.scss @@ -71,24 +71,40 @@ section.course-content { background: #c2c2c2; border: none; border-bottom: 1px solid #000; - height: 14px; + height: 7px; + @include transition(); a.ui-slider-handle { - @include border-radius(20px); + @include border-radius(15px); @include box-shadow(inset 0 1px 0 lighten($mit-red, 10%)); background: $mit-red url(/static/images/slider-handle.png) center center no-repeat; border: 1px solid darken($mit-red, 20%); cursor: pointer; - height: 20px; - margin-left: -10px; + height: 15px; + margin-left: -7px; top: -4px; - width: 20px; + width: 15px; + @include transition(); + @include background-size(50%); &:focus, &:hover { background-color: lighten($mit-red, 10%); outline: none; } } + + &:hover { + height: 14px; + margin-top: -7px; + + a.ui-slider-handle { + @include border-radius(20px); + height: 20px; + margin-left: -10px; + top: -4px; + width: 20px; + } + } } ul.vcr { @@ -141,38 +157,67 @@ section.course-content { float: right; div.speeds { - border-right: 1px solid #000; border-left: 1px solid #000; + border-right: 1px solid #000; @include box-shadow(1px 0 0 #555, inset 1px 0 0 #555); float: left; line-height: 46px; //height of play pause buttons margin-right: 0; - -webkit-font-smoothing: antialiased; - opacity: .7; + position: relative; @include transition(); + width: 110px; + -webkit-font-smoothing: antialiased; h3 { @include inline-block(); - padding: 0 lh(.5); + padding: 0 lh(.25) 0 lh(.5); font-weight: normal; + text-transform: uppercase; + font-size: 12px; + letter-spacing: 1px; + color: #999; + } + + p.active { + @include inline-block(); + padding: 0 lh(.5) 0 0; + margin-bottom: 0; + font-weight: bold; } // fix for now ol#video_speeds { + background-color: #444; + border: 1px solid #000; + border-top: 0; + @include box-shadow(inset 1px 0 0 #555); @include inline-block(); - padding-right: lh(.5); + left: -1px; + position: absolute; + top: 48px; + width: 100%; + z-index: 10; li { - cursor: pointer; + border-bottom: 1px solid #000; + @include box-shadow( 0 1px 0 #555); color: #fff; - @include inline-block(); + cursor: pointer; + padding: 0 lh(.5); &.active { font-weight: bold; } + &:last-child { + border-bottom: 0; + margin-top: 0; + @include box-shadow(none); + } + &:hover { color: #aaa; + background-color: #666; } } } diff --git a/templates/video.html b/templates/video.html index 0f82fa5b8c..bb7d1b9ed0 100644 --- a/templates/video.html +++ b/templates/video.html @@ -29,6 +29,7 @@

    Speed

    +

      @@ -66,13 +67,13 @@ $('div.video-subtitles').toggleClass('closed'); $(this).toggleClass("off"); - // var link_text = $('.hide-subtitles').text(); - // $(this).text((link_text == 'turn off') ? 'turn on' : 'turn off'); return false; }); - $("div.speeds").click(function(){ - $("div.speeds ol#video_speeds").slideToggle(); + $("ol#video_speeds").hide(); + + $("div.speeds").click(function() { + $("ol#video_speeds").slideToggle(); }); }); diff --git a/templates/video_init.js b/templates/video_init.js index 787a9be5ad..c9fe437888 100644 --- a/templates/video_init.js +++ b/templates/video_init.js @@ -38,6 +38,7 @@ function add_speed(key, stream) { if (key == video_speed) { $("#video_speeds").append('
    1. '+key+'x
    2. '); + $("p.active").text(key + 'x'); } else { $("#video_speeds").append('
    3. '+key+'x
    4. '); } @@ -46,6 +47,8 @@ function add_speed(key, stream) { change_video_speed(key, stream); $(this).siblings().removeClass("active"); $(this).addClass("active"); + var active = $(this).text(); + $("p.active").text(active); }); } From 26c6fe29c19240b8555c0f5b103f4e8a010f84fd Mon Sep 17 00:00:00 2001 From: Kyle Fiedler Date: Tue, 3 Apr 2012 14:00:18 -0400 Subject: [PATCH 34/50] Added tooltip for time on video --- static/css/application.css | 117 ++++++++++++++++++- static/js/jquery.qtip.min.js | 13 +++ templates/main.html | 1 + templates/sass/application.scss | 2 +- templates/sass/courseware/_video.scss | 9 ++ templates/sass/plugins/_jquery.qtip.min.scss | 1 + templates/video_init.js | 35 +++++- 7 files changed, 172 insertions(+), 6 deletions(-) create mode 100644 static/js/jquery.qtip.min.js create mode 100644 templates/sass/plugins/_jquery.qtip.min.scss diff --git a/static/css/application.css b/static/css/application.css index ee4fa0cc06..869571bdf2 100644 --- a/static/css/application.css +++ b/static/css/application.css @@ -2556,6 +2556,93 @@ button.ui-button::-moz-focus-inner { margin: -1px; height: 100%; } +.ui-tooltip, .qtip { + position: absolute; + left: -28000px; + top: -28000px; + display: none; + max-width: 280px; + min-width: 50px; + font-size: 10.5px; + line-height: 12px; } + +.ui-tooltip-fluid { + display: block; + visibility: hidden; + position: static!important; + float: left!important; } + +.ui-tooltip-content { + position: relative; + padding: 5px 9px; + overflow: hidden; + border: 1px solid #000001; + text-align: left; + word-wrap: break-word; + overflow: hidden; } + +.ui-tooltip-titlebar { + position: relative; + min-height: 14px; + padding: 5px 35px 5px 10px; + overflow: hidden; + border: 1px solid #000001; + border-width: 1px 1px 0; + font-weight: bold; } + +.ui-tooltip-titlebar + .ui-tooltip-content { + border-top-width: 0!important; } + +/*Default close button class */ +.ui-tooltip-titlebar .ui-state-default { + position: absolute; + right: 4px; + top: 50%; + margin-top: -9px; + cursor: pointer; + outline: medium none; + border-width: 1px; + border-style: solid; } + +* html .ui-tooltip-titlebar .ui-state-default { + top: 16px; } + +.ui-tooltip-titlebar .ui-icon, .ui-tooltip-icon .ui-icon { + display: block; + text-indent: -1000em; } + +.ui-tooltip-icon, .ui-tooltip-icon .ui-icon { + -moz-border-radius: 3px; + -webkit-border-radius: 3px; + border-radius: 3px; } + +.ui-tooltip-icon .ui-icon { + width: 18px; + height: 14px; + text-align: center; + text-indent: 0; + font: normal bold 10px/13px Tahoma, sans-serif; + color: inherit; + background: transparent none no-repeat -100em -100em; } + +/*Default tooltip style */ +.ui-tooltip-default .ui-tooltip-titlebar, .ui-tooltip-default .ui-tooltip-content { + border-color: #F1D031; + background-color: #FFFFA3; + color: #555; } + +.ui-tooltip-default .ui-tooltip-titlebar { + background-color: #FFEF93; } + +.ui-tooltip-default .ui-tooltip-icon { + border-color: #CCC; + background: #F1F1F1; + color: #777; } + +.ui-tooltip-default .ui-tooltip-titlebar .ui-state-hover { + border-color: #AAA; + color: #111; } + div.course-wrapper ul, div.course-wrapper ol { list-style: none; } div.course-wrapper section.course-content p { @@ -2817,6 +2904,7 @@ section.course-content div.video-subtitles div.video-wrapper section.video-contr background: #333; position: relative; border: 1px solid #000; + border-top: 0; color: #ccc; } section.course-content div.video-subtitles div.video-wrapper section.video-controls div#slider { -webkit-border-radius: 0; @@ -2829,6 +2917,7 @@ section.course-content div.video-subtitles div.video-wrapper section.video-contr box-shadow: inset 0 1px 0 #eeeeee, 0 1px 0 #555555; background: #c2c2c2; border: none; + border-top: 1px solid #000; border-bottom: 1px solid #000; height: 7px; -webkit-transition-property: all; @@ -2851,6 +2940,11 @@ section.course-content div.video-subtitles div.video-wrapper section.video-contr -ms-transition-delay: 0; -o-transition-delay: 0; transition-delay: 0; } +section.course-content div.video-subtitles div.video-wrapper section.video-controls div#slider div.ui-widget-header { + background: #777; + -webkit-box-shadow: inset 0 1px 0 #999999; + -moz-box-shadow: inset 0 1px 0 #999999; + box-shadow: inset 0 1px 0 #999999; } section.course-content div.video-subtitles div.video-wrapper section.video-controls div#slider a.ui-slider-handle { -webkit-border-radius: 15px; -moz-border-radius: 15px; @@ -2925,7 +3019,27 @@ section.course-content div.video-subtitles div.video-wrapper section.video-contr height: 14px; padding: 16.989px; text-indent: -9999px; - width: 14px; } + width: 14px; + -webkit-transition-property: all; + -moz-transition-property: all; + -ms-transition-property: all; + -o-transition-property: all; + transition-property: all; + -webkit-transition-duration: 0.15s; + -moz-transition-duration: 0.15s; + -ms-transition-duration: 0.15s; + -o-transition-duration: 0.15s; + transition-duration: 0.15s; + -webkit-transition-timing-function: ease-out; + -moz-transition-timing-function: ease-out; + -ms-transition-timing-function: ease-out; + -o-transition-timing-function: ease-out; + transition-timing-function: ease-out; + -webkit-transition-delay: 0; + -moz-transition-delay: 0; + -ms-transition-delay: 0; + -o-transition-delay: 0; + transition-delay: 0; } section.course-content div.video-subtitles div.video-wrapper section.video-controls ul.vcr li a.play { background: url("/static/images/play-icon.png") center center no-repeat; } section.course-content div.video-subtitles div.video-wrapper section.video-controls ul.vcr li a.play:hover { @@ -2972,6 +3086,7 @@ section.course-content div.video-subtitles div.video-wrapper section.video-contr -o-transition-delay: 0; transition-delay: 0; width: 110px; + cursor: pointer; -webkit-font-smoothing: antialiased; } section.course-content div.video-subtitles div.video-wrapper section.video-controls div.secondary-controls div.speeds h3, section.course-content div.video-subtitles div.video-wrapper section.video-controls div.secondary-controls div.speeds div#wiki_panel input[type="button"], div#wiki_panel section.course-content div.video-subtitles div.video-wrapper section.video-controls div.secondary-controls div.speeds input[type="button"] { display: -moz-inline-box; diff --git a/static/js/jquery.qtip.min.js b/static/js/jquery.qtip.min.js new file mode 100644 index 0000000000..638da26081 --- /dev/null +++ b/static/js/jquery.qtip.min.js @@ -0,0 +1,13 @@ +/* +* qTip2 - Pretty powerful tooltips +* http://craigsworks.com/projects/qtip2/ +* +* Version: nightly +* Copyright 2009-2010 Craig Michael Thompson - http://craigsworks.com +* +* Dual licensed under MIT or GPLv2 licenses +* http://en.wikipedia.org/wiki/MIT_License +* http://en.wikipedia.org/wiki/GNU_General_Public_License +* +* Date: Mon Apr 2 13:46:17.0000000000 2012 +*//*jslint browser: true, onevar: true, undef: true, nomen: true, bitwise: true, regexp: true, newcap: true, immed: true, strict: true *//*global window: false, jQuery: false, console: false, define: false */(function(a){typeof define==="function"&&define.amd?define(["jquery"],a):a(jQuery)})(function(a){function z(d){var e=this,f=d.elements.tooltip,g=d.options.content.ajax,h=".qtip-ajax",i=/)<[^<]*)*<\/script>/gi,j=b,k=c,l;d.checks.ajax={"^content.ajax":function(a,b,c){b==="ajax"&&(g=c),b==="once"?e.init():g&&g.url?e.load():f.unbind(h)}},a.extend(e,{init:function(){g&&g.url&&f.unbind(h)[g.once?"one":"bind"]("tooltipshow"+h,e.load);return e},load:function(b,f){function r(a,b,c){!k&&a.status!==0&&d.set("content.text",b+": "+c)}function q(b){k||(m&&(b=a("
      ").append(b.replace(i,"")).find(m)),d.set("content.text",b))}function p(){k||(n&&(d.show(b.originalEvent),f=c),a.isFunction(g.complete)&&g.complete.apply(this,arguments))}var h=g.url.indexOf(" "),j=g.url,m,n=g.once&&!g.loading&&f;if(n)try{b.preventDefault()}catch(o){}else if(b&&b.isDefaultPrevented())return e;l&&l.abort&&l.abort(),h>-1&&(m=j.substr(h),j=j.substr(0,h)),l=a.ajax(a.extend({success:q,error:r,context:d},g,{url:j,complete:p}))},destroy:function(){l&&l.abort&&l.abort(),k=b}}),e.init()}function y(e,h){var i,j,k,l,m,n=a(this),o=a(document.body),p=this===document?o:n,q=n.metadata?n.metadata(h.metadata):d,r=h.metadata.type==="html5"&&q?q[h.metadata.name]:d,s=n.data(h.metadata.name||"qtipopts");try{s=typeof s==="string"?(new Function("return "+s))():s}catch(u){v("Unable to parse HTML5 attribute data: "+s)}l=a.extend(b,{},f.defaults,h,typeof s==="object"?w(s):d,w(r||q)),j=l.position,l.id=e;if("boolean"===typeof l.content.text){k=n.attr(l.content.attr);if(l.content.attr!==c&&k)l.content.text=k;else{v("Unable to locate content for tooltip! Aborting render of tooltip on element: ",n);return c}}j.container.length||(j.container=o),j.target===c&&(j.target=p),l.show.target===c&&(l.show.target=p),l.show.solo===b&&(l.show.solo=j.container.closest("body")),l.hide.target===c&&(l.hide.target=p),l.position.viewport===b&&(l.position.viewport=j.container),j.container=j.container.eq(0),j.at=new g.Corner(j.at),j.my=new g.Corner(j.my);if(a.data(this,"qtip"))if(l.overwrite)n.qtip("destroy");else if(l.overwrite===c)return c;l.suppress&&(m=a.attr(this,"title"))&&a(this).removeAttr("title").attr(t,m),i=new x(n,l,e,!!k),a.data(this,"qtip",i),n.bind("remove.qtip-"+e+" removeqtip.qtip-"+e,function(){i.destroy()});return i}function x(r,s,v,x){function Q(){var b=[s.show.target[0],s.hide.target[0],y.rendered&&F.tooltip[0],s.position.container[0],s.position.viewport[0],window,document];y.rendered?a([]).pushStack(a.grep(b,function(a){return typeof a==="object"})).unbind(E):s.show.target.unbind(E+"-create")}function P(){function o(a){D.is(":visible")&&y.reposition(a)}function n(a){if(D.hasClass(l))return c;clearTimeout(y.timers.inactive),y.timers.inactive=setTimeout(function(){y.hide(a)},s.hide.inactive)}function k(b){if(D.hasClass(l)||B||C)return c;var f=a(b.relatedTarget||b.target),g=f.closest(m)[0]===D[0],h=f[0]===e.show[0];clearTimeout(y.timers.show),clearTimeout(y.timers.hide);if(d.target==="mouse"&&g||s.hide.fixed&&(/mouse(out|leave|move)/.test(b.type)&&(g||h)))try{b.preventDefault(),b.stopImmediatePropagation()}catch(i){}else s.hide.delay>0?y.timers.hide=setTimeout(function(){y.hide(b)},s.hide.delay):y.hide(b)}function j(a){if(D.hasClass(l))return c;clearTimeout(y.timers.show),clearTimeout(y.timers.hide);var d=function(){y.toggle(b,a)};s.show.delay>0?y.timers.show=setTimeout(d,s.show.delay):d()}var d=s.position,e={show:s.show.target,hide:s.hide.target,viewport:a(d.viewport),document:a(document),body:a(document.body),window:a(window)},g={show:a.trim(""+s.show.event).split(" "),hide:a.trim(""+s.hide.event).split(" ")},i=a.browser.msie&&parseInt(a.browser.version,10)===6;D.bind("mouseenter"+E+" mouseleave"+E,function(a){var b=a.type==="mouseenter";b&&y.focus(a),D.toggleClass(p,b)}),s.hide.fixed&&(e.hide=e.hide.add(D),D.bind("mouseover"+E,function(){D.hasClass(l)||clearTimeout(y.timers.hide)})),/mouse(out|leave)/i.test(s.hide.event)?s.hide.leave==="window"&&e.window.bind("mouseout"+E+" blur"+E,function(a){/select|option/.test(a.target)&&!a.relatedTarget&&y.hide(a)}):/mouse(over|enter)/i.test(s.show.event)&&e.hide.bind("mouseleave"+E,function(a){clearTimeout(y.timers.show)}),(""+s.hide.event).indexOf("unfocus")>-1&&d.container.closest("html").bind("mousedown"+E,function(b){var c=a(b.target),d=!D.hasClass(l)&&D.is(":visible"),e=c.parents(m).filter(D[0]).length>0;c[0]!==r[0]&&c[0]!==D[0]&&!e&&!r.has(c[0]).length&&!c.attr("disabled")&&y.hide(b)}),"number"===typeof s.hide.inactive&&(e.show.bind("qtip-"+v+"-inactive",n),a.each(f.inactiveEvents,function(a,b){e.hide.add(F.tooltip).bind(b+E+"-inactive",n)})),a.each(g.hide,function(b,c){var d=a.inArray(c,g.show),f=a(e.hide);d>-1&&f.add(e.show).length===f.length||c==="unfocus"?(e.show.bind(c+E,function(a){D.is(":visible")?k(a):j(a)}),delete g.show[d]):e.hide.bind(c+E,k)}),a.each(g.show,function(a,b){e.show.bind(b+E,j)}),"number"===typeof s.hide.distance&&e.show.add(D).bind("mousemove"+E,function(a){var b=G.origin||{},c=s.hide.distance,d=Math.abs;(d(a.pageX-b.pageX)>=c||d(a.pageY-b.pageY)>=c)&&y.hide(a)}),d.target==="mouse"&&(e.show.bind("mousemove"+E,function(a){h={pageX:a.pageX,pageY:a.pageY,type:"mousemove"}}),d.adjust.mouse&&(s.hide.event&&(D.bind("mouseleave"+E,function(a){(a.relatedTarget||a.target)!==e.show[0]&&y.hide(a)}),F.target.bind("mouseenter"+E+" mouseleave"+E,function(a){G.onTarget=a.type==="mouseenter"})),e.document.bind("mousemove"+E,function(a){G.onTarget&&!D.hasClass(l)&&D.is(":visible")&&y.reposition(a||h)}))),(d.adjust.resize||e.viewport.length)&&(a.event.special.resize?e.viewport:e.window).bind("resize"+E,o),(e.viewport.length||i&&D.css("position")==="fixed")&&e.viewport.bind("scroll"+E,o)}function O(b,d){function g(b){function i(e){e&&(delete h[e.src],clearTimeout(y.timers.img[e.src]),a(e).unbind(E)),a.isEmptyObject(h)&&(y.redraw(),d!==c&&y.reposition(G.event),b())}var g,h={};if((g=f.find("img[src]:not([height]):not([width])")).length===0)return i();g.each(function(b,c){if(h[c.src]===e){var d=0,f=3;(function g(){if(c.height||c.width||d>f)return i(c);d+=1,y.timers.img[c.src]=setTimeout(g,700)})(),a(c).bind("error"+E+" load"+E,function(){i(this)}),h[c.src]=c}})}var f=F.content;if(!y.rendered||!b)return c;a.isFunction(b)&&(b=b.call(r,G.event,y)||""),b.jquery&&b.length>0?f.empty().append(b.css({display:"block"})):f.html(b),y.rendered<0?D.queue("fx",g):(C=0,g(a.noop));return y}function N(b,d){var e=F.title;if(!y.rendered||!b)return c;a.isFunction(b)&&(b=b.call(r,G.event,y));if(b===c||!b&&b!=="")return J(c);b.jquery&&b.length>0?e.empty().append(b.css({display:"block"})):e.html(b),y.redraw(),d!==c&&y.rendered&&D.is(":visible")&&y.reposition(G.event)}function M(a){var b=F.button,d=F.title;if(!y.rendered)return c;a?(d||L(),K()):b.remove()}function L(){var c=A+"-title";F.titlebar&&J(),F.titlebar=a("
      ",{"class":j+"-titlebar "+(s.style.widget?"ui-widget-header":"")}).append(F.title=a("
      ",{id:c,"class":j+"-title","aria-atomic":b})).insertBefore(F.content).delegate(".ui-tooltip-close","mousedown keydown mouseup keyup mouseout",function(b){a(this).toggleClass("ui-state-active ui-state-focus",b.type.substr(-4)==="down")}).delegate(".ui-tooltip-close","mouseover mouseout",function(b){a(this).toggleClass("ui-state-hover",b.type==="mouseover")}),s.content.title.button?K():y.rendered&&y.redraw()}function K(){var b=s.content.title.button,d=typeof b==="string",e=d?b:"Close tooltip";F.button&&F.button.remove(),b.jquery?F.button=b:F.button=a("",{"class":"ui-state-default ui-tooltip-close "+(s.style.widget?"":j+"-icon"),title:e,"aria-label":e}).prepend(a("",{"class":"ui-icon ui-icon-close",html:"×"})),F.button.appendTo(F.titlebar).attr("role","button").click(function(a){D.hasClass(l)||y.hide(a);return c}),y.redraw()}function J(a){F.title&&(F.titlebar.remove(),F.titlebar=F.title=F.button=d,a!==c&&y.reposition())}function I(){var a=s.style.widget;D.toggleClass(k,a).toggleClass(n,s.style.def&&!a),F.content.toggleClass(k+"-content",a),F.titlebar&&F.titlebar.toggleClass(k+"-header",a),F.button&&F.button.toggleClass(j+"-icon",!a)}function H(a){var b=0,c,d=s,e=a.split(".");while(d=d[e[b++]])b0&&!a("#"+i).length&&(D[0].id=i,F.content[0].id=i+"-content",F.title[0].id=i+"-title")},"^content.text$":function(a,b,c){O(c)},"^content.title.text$":function(a,b,c){if(!c)return J();!F.title&&c&&L(),N(c)},"^content.title.button$":function(a,b,c){M(c)},"^position.(my|at)$":function(a,b,c){"string"===typeof c&&(a[b]=new g.Corner(c))},"^position.container$":function(a,b,c){y.rendered&&D.appendTo(c)},"^show.ready$":function(){y.rendered?y.toggle(b):y.render(1)},"^style.classes$":function(a,b,c){D.attr("class",j+" qtip ui-helper-reset "+c)},"^style.widget|content.title":I,"^events.(render|show|move|hide|focus|blur)$":function(b,c,d){D[(a.isFunction(d)?"":"un")+"bind"]("tooltip"+c,d)},"^(show|hide|position).(event|target|fixed|inactive|leave|distance|viewport|adjust)":function(){var a=s.position;D.attr("tracking",a.target==="mouse"&&a.adjust.mouse),Q(),P()}},a.extend(y,{render:function(d){if(y.rendered)return y;var e=s.content.text,f=s.content.title.text,h=s.position,i=a.Event("tooltiprender");a.attr(r[0],"aria-describedby",A),D=F.tooltip=a("
      ",{id:A,"class":j+" qtip ui-helper-reset "+n+" "+s.style.classes+" "+j+"-pos-"+s.position.my.abbrev(),width:s.style.width||"",height:s.style.height||"",tracking:h.target==="mouse"&&h.adjust.mouse,role:"alert","aria-live":"polite","aria-atomic":c,"aria-describedby":A+"-content","aria-hidden":b}).toggleClass(l,G.disabled).data("qtip",y).appendTo(s.position.container).append(F.content=a("
      ",{"class":j+"-content",id:A+"-content","aria-atomic":b})),y.rendered=-1,B=C=1,f&&(L(),a.isFunction(f)||N(f,c)),a.isFunction(e)||O(e,c),y.rendered=b,I(),a.each(s.events,function(b,c){a.isFunction(c)&&D.bind(b==="toggle"?"tooltipshow tooltiphide":"tooltip"+b,c)}),a.each(g,function(){this.initialize==="render"&&this(y)}),P(),D.queue("fx",function(a){i.originalEvent=G.event,D.trigger(i,[y]),B=C=0,y.redraw(),(s.show.ready||d)&&y.toggle(b,G.event,c),a()});return y},get:function(a){var b,c;switch(a.toLowerCase()){case"dimensions":b={height:D.outerHeight(),width:D.outerWidth()};break;case"offset":b=g.offset(D,s.position.container);break;default:c=H(a.toLowerCase()),b=c[0][c[1]],b=b.precedance?b.string():b}return b},set:function(e,f){function m(a,b){var c,d,e;for(c in k)for(d in k[c])if(e=(new RegExp(d,"i")).exec(a))b.push(e),k[c][d].apply(y,b)}var g=/^position\.(my|at|adjust|target|container)|style|content|show\.ready/i,h=/^content\.(title|attr)|style/i,i=c,j=c,k=y.checks,l;"string"===typeof e?(l=e,e={},e[l]=f):e=a.extend(b,{},e),a.each(e,function(b,c){var d=H(b.toLowerCase()),f;f=d[0][d[1]],d[0][d[1]]="object"===typeof c&&c.nodeType?a(c):c,e[b]=[d[0],d[1],c,f],i=g.test(b)||i,j=h.test(b)||j}),w(s),B=C=1,a.each(e,m),B=C=0,D.is(":visible")&&y.rendered&&(i&&y.reposition(s.position.target==="mouse"?d:G.event),j&&y.redraw());return y},toggle:function(e,f){function r(){e?(a.browser.msie&&D[0].style.removeAttribute("filter"),D.css("overflow",""),"string"===typeof i.autofocus&&a(i.autofocus,D).focus(),i.target.trigger("qtip-"+v+"-inactive")):D.css({display:"",visibility:"",opacity:"",left:"",top:""}),q=a.Event("tooltip"+(e?"visible":"hidden")),q.originalEvent=f?G.event:d,D.trigger(q,[y])}if(!y.rendered)return e?y.render(1):y;var g=e?"show":"hide",i=s[g],j=D.is(":visible"),k=!f||i.target.length<2||G.target[0]===f.target,l=f&&i.target.add(f.target).length!==i.target.length,n=s.position,o=s.content,p,q;(typeof e).search("boolean|number")&&(e=!j);if(!D.is(":animated")&&j===e&&k)return y;if(f){if(/over|enter/.test(f.type)&&/out|leave/.test(G.event.type)&&s.show.target.add(f.target).length===s.show.target.length&&D.has(f.relatedTarget).length)return y;G.event=a.extend({},f)}q=a.Event("tooltip"+g),q.originalEvent=f?G.event:d,D.trigger(q,[y,90]);if(q.isDefaultPrevented())return y;a.attr(D[0],"aria-hidden",!e),e?(G.origin=a.extend({},h),y.focus(f),a.isFunction(o.text)&&O(o.text,c),a.isFunction(o.title.text)&&N(o.title.text,c),!u&&n.target==="mouse"&&n.adjust.mouse&&(a(document).bind("mousemove.qtip",function(a){h={pageX:a.pageX,pageY:a.pageY,type:"mousemove"}}),u=b),y.reposition(f,arguments[2]),(q.solo=!!i.solo)&&a(m,i.solo).not(D).qtip("hide",q)):(clearTimeout(y.timers.show),delete G.origin,u&&!a(m+'[tracking="true"]:visible',i.solo).not(D).length&&(a(document).unbind("mousemove.qtip"),u=c),y.blur(f)),D.stop(l,!l),i.effect===c?(D[g](),r.call(D)):a.isFunction(i.effect)?(i.effect.call(D,y),D.queue("fx",function(a){r(),a()})):D.fadeTo(90,e?1:0,r),e&&i.target.trigger("qtip-"+v+"-inactive");return y},show:function(a){return y.toggle(b,a)},hide:function(a){return y.toggle(c,a)},focus:function(b){if(!y.rendered)return y;var c=a(m),d=parseInt(D[0].style.zIndex,10),e=f.zindex+c.length,g=a.extend({},b),h,i;D.hasClass(o)||(i=a.Event("tooltipfocus"),i.originalEvent=g,D.trigger(i,[y,e]),i.isDefaultPrevented()||(d!==e&&(c.each(function(){this.style.zIndex>d&&(this.style.zIndex=this.style.zIndex-1)}),c.filter("."+o).qtip("blur",g)),D.addClass(o)[0].style.zIndex=e));return y},blur:function(b){var c=a.extend({},b),d;D.removeClass(o),d=a.Event("tooltipblur"),d.originalEvent=c,D.trigger(d,[y]);return y},reposition:function(b,d){if(!y.rendered||B)return y;B=1;var e=s.position.target,f=s.position,i=f.my,k=f.at,l=f.adjust,m=l.method.split(" "),n=D.outerWidth(),o=D.outerHeight(),p=0,q=0,r=a.Event("tooltipmove"),t=D.css("position")==="fixed",u=f.viewport,v={left:0,top:0},w=f.container,x=c,A=y.plugins.tip,C={horizontal:m[0],vertical:m[1]=m[1]||m[0],enabled:u.jquery&&e[0]!==window&&e[0]!==z&&l.method!=="none",left:function(a){var b=C.horizontal==="shift",c=-w.offset.left+u.offset.left+u.scrollLeft,d=i.x==="left"?n:i.x==="right"?-n:-n/2,e=k.x==="left"?p:k.x==="right"?-p:-p/2,f=A&&A.size?A.size.width||0:0,g=A&&A.corner&&A.corner.precedance==="x"&&!b?f:0,h=c-a+g,j=a+n-u.width-c+g,m=d-(i.precedance==="x"||i.x===i.y?e:0)-(k.x==="center"?p/2:0),o=i.x==="center";b?(g=A&&A.corner&&A.corner.precedance==="y"?f:0,m=(i.x==="left"?1:-1)*d-g,v.left+=h>0?h:j>0?-j:0,v.left=Math.max(-w.offset.left+u.offset.left+(g&&A.corner.x==="center"?A.offset:0),a-m,Math.min(Math.max(-w.offset.left+u.offset.left+u.width,a+m),v.left))):(h>0&&(i.x!=="left"||j>0)?v.left-=m:j>0&&(i.x!=="right"||h>0)&&(v.left-=o?-m:m),v.left!==a&&o&&(v.left-=l.x),v.leftj&&(v.left=a));return v.left-a},top:function(a){var b=C.vertical==="shift",c=-w.offset.top+u.offset.top+u.scrollTop,d=i.y==="top"?o:i.y==="bottom"?-o:-o/2,e=k.y==="top"?q:k.y==="bottom"?-q:-q/2,f=A&&A.size?A.size.height||0:0,g=A&&A.corner&&A.corner.precedance==="y"&&!b?f:0,h=c-a+g,j=a+o-u.height-c+g,m=d-(i.precedance==="y"||i.x===i.y?e:0)-(k.y==="center"?q/2:0),n=i.y==="center";b?(g=A&&A.corner&&A.corner.precedance==="x"?f:0,m=(i.y==="top"?1:-1)*d-g,v.top+=h>0?h:j>0?-j:0,v.top=Math.max(-w.offset.top+u.offset.top+(g&&A.corner.x==="center"?A.offset:0),a-m,Math.min(Math.max(-w.offset.top+u.offset.top+u.height,a+m),v.top))):(h>0&&(i.y!=="top"||j>0)?v.top-=m:j>0&&(i.y!=="bottom"||h>0)&&(v.top-=n?-m:m),v.top!==a&&n&&(v.top-=l.y),v.top<0&&-v.top>j&&(v.top=a));return v.top-a}},E;if(a.isArray(e)&&e.length===2)k={x:"left",y:"top"},v={left:e[0],top:e[1]};else if(e==="mouse"&&(b&&b.pageX||G.event.pageX))k={x:"left",y:"top"},b=(b&&(b.type==="resize"||b.type==="scroll")?G.event:b&&b.pageX&&b.type==="mousemove"?b:h&&h.pageX&&(l.mouse||!b||!b.pageX)?{pageX:h.pageX,pageY:h.pageY}:!l.mouse&&G.origin&&G.origin.pageX&&s.show.distance?G.origin:b)||b||G.event||h||{},v={top:b.pageY,left:b.pageX};else{e==="event"?b&&b.target&&b.type!=="scroll"&&b.type!=="resize"?e=G.target=a(b.target):e=G.target:e=G.target=a(e.jquery?e:F.target),e=a(e).eq(0);if(e.length===0)return y;e[0]===document||e[0]===window?(p=g.iOS?window.innerWidth:e.width(),q=g.iOS?window.innerHeight:e.height(),e[0]===window&&(v={top:(u||e).scrollTop(),left:(u||e).scrollLeft()})):e.is("area")&&g.imagemap?v=g.imagemap(e,k,C.enabled?m:c):e[0].namespaceURI==="http://www.w3.org/2000/svg"&&g.svg?v=g.svg(e,k):(p=e.outerWidth(),q=e.outerHeight(),v=g.offset(e,w)),v.offset&&(p=v.width,q=v.height,x=v.flipoffset,v=v.offset);if(g.iOS<4.1&&g.iOS>3.1||g.iOS==4.3||!g.iOS&&t)E=a(window),v.left-=E.scrollLeft(),v.top-=E.scrollTop();v.left+=k.x==="right"?p:k.x==="center"?p/2:0,v.top+=k.y==="bottom"?q:k.y==="center"?q/2:0}v.left+=l.x+(i.x==="right"?-n:i.x==="center"?-n/2:0),v.top+=l.y+(i.y==="bottom"?-o:i.y==="center"?-o/2:0),C.enabled?(u={elem:u,height:u[(u[0]===window?"h":"outerH")+"eight"](),width:u[(u[0]===window?"w":"outerW")+"idth"](),scrollLeft:t?0:u.scrollLeft(),scrollTop:t?0:u.scrollTop(),offset:u.offset()||{left:0,top:0}},w={elem:w,scrollLeft:w.scrollLeft(),scrollTop:w.scrollTop(),offset:w.offset()||{left:0,top:0}},v.adjusted={left:C.horizontal!=="none"?C.left(v.left):0,top:C.vertical!=="none"?C.top(v.top):0},v.adjusted.left+v.adjusted.top&&D.attr("class",D[0].className.replace(/ui-tooltip-pos-\w+/i,j+"-pos-"+i.abbrev())),x&&v.adjusted.left&&(v.left+=x.left),x&&v.adjusted.top&&(v.top+=x.top)):v.adjusted={left:0,top:0},r.originalEvent=a.extend({},b),D.trigger(r,[y,v,u.elem||u]);if(r.isDefaultPrevented())return y;delete v.adjusted,d===c||isNaN(v.left)||isNaN(v.top)||e==="mouse"||!a.isFunction(f.effect)?D.css(v):a.isFunction(f.effect)&&(f.effect.call(D,y,a.extend({},v)),D.queue(function(b){a(this).css({opacity:"",height:""}),a.browser.msie&&this.style.removeAttribute("filter"),b()})),B=0;return y},redraw:function(){if(y.rendered<1||C)return y;var a=s.position.container,b,c,d,e;C=1,s.style.height&&D.css("height",s.style.height),s.style.width?D.css("width",s.style.width):(D.css("width","").addClass(q),c=D.width()+1,d=D.css("max-width")||"",e=D.css("min-width")||"",b=(d+e).indexOf("%")>-1?a.width()/100:0,d=(d.indexOf("%")>-1?b:1)*parseInt(d,10)||c,e=(e.indexOf("%")>-1?b:1)*parseInt(e,10)||0,c=d+e?Math.min(Math.max(c,e),d):c,D.css("width",Math.round(c)).removeClass(q)),C=0;return y},disable:function(b){"boolean"!==typeof b&&(b=!D.hasClass(l)&&!G.disabled),y.rendered?(D.toggleClass(l,b),a.attr(D[0],"aria-disabled",b)):G.disabled=!!b;return y},enable:function(){return y.disable(c)},destroy:function(){var b=r[0],c=a.attr(b,t),d=r.data("qtip");y.rendered&&(D.stop(1,0).remove(),a.each(y.plugins,function(){this.destroy&&this.destroy()})),clearTimeout(y.timers.show),clearTimeout(y.timers.hide),Q();if(!d||y===d)a.removeData(b,"qtip"),s.suppress&&c&&(a.attr(b,"title",c),r.removeAttr(t)),r.removeAttr("aria-describedby");r.unbind(".qtip-"+v),delete i[y.id];return r}})}function w(b){var e;if(!b||"object"!==typeof b)return c;if(b.metadata===d||"object"!==typeof b.metadata)b.metadata={type:b.metadata};if("content"in b){if(b.content===d||"object"!==typeof b.content||b.content.jquery)b.content={text:b.content};e=b.content.text||c,!a.isFunction(e)&&(!e&&!e.attr||e.length<1||"object"===typeof e&&!e.jquery)&&(b.content.text=c);if("title"in b.content){if(b.content.title===d||"object"!==typeof b.content.title)b.content.title={text:b.content.title};e=b.content.title.text||c,!a.isFunction(e)&&(!e&&!e.attr||e.length<1||"object"===typeof e&&!e.jquery)&&(b.content.title.text=c)}}if("position"in b)if(b.position===d||"object"!==typeof b.position)b.position={my:b.position,at:b.position};if("show"in b)if(b.show===d||"object"!==typeof b.show)b.show.jquery?b.show={target:b.show}:b.show={event:b.show};if("hide"in b)if(b.hide===d||"object"!==typeof b.hide)b.hide.jquery?b.hide={target:b.hide}:b.hide={event:b.hide};if("style"in b)if(b.style===d||"object"!==typeof b.style)b.style={classes:b.style};a.each(g,function(){this.sanitize&&this.sanitize(b)});return b}function v(){v.history=v.history||[],v.history.push(arguments);if("object"===typeof console){var a=console[console.warn?"warn":"log"],b=Array.prototype.slice.call(arguments),c;typeof arguments[0]==="string"&&(b[0]="qTip2: "+b[0]),c=a.apply?a.apply(console,b):a(b)}}"use strict";var b=!0,c=!1,d=null,e,f,g,h,i={},j="ui-tooltip",k="ui-widget",l="ui-state-disabled",m="div.qtip."+j,n=j+"-default",o=j+"-focus",p=j+"-hover",q=j+"-fluid",r="-31000px",s="_replacedByqTip",t="oldtitle",u;f=a.fn.qtip=function(g,h,i){var j=(""+g).toLowerCase(),k=d,l=a.makeArray(arguments).slice(1),m=l[l.length-1],n=this[0]?a.data(this[0],"qtip"):d;if(!arguments.length&&n||j==="api")return n;if("string"===typeof g){this.each(function(){var d=a.data(this,"qtip");if(!d)return b;m&&m.timeStamp&&(d.cache.event=m);if(j!=="option"&&j!=="options"||!h)d[j]&&d[j].apply(d[j],l);else if(a.isPlainObject(h)||i!==e)d.set(h,i);else{k=d.get(h);return c}});return k!==d?k:this}if("object"===typeof g||!arguments.length){n=w(a.extend(b,{},g));return f.bind.call(this,n,m)}},f.bind=function(d,j){return this.each(function(k){function r(b){function d(){p.render(typeof b==="object"||l.show.ready),m.show.add(m.hide).unbind(o)}if(p.cache.disabled)return c;p.cache.event=a.extend({},b),p.cache.target=b?a(b.target):[e],l.show.delay>0?(clearTimeout(p.timers.show),p.timers.show=setTimeout(d,l.show.delay),n.show!==n.hide&&m.hide.bind(n.hide,function(){clearTimeout(p.timers.show)})):d()}var l,m,n,o,p,q;q=a.isArray(d.id)?d.id[k]:d.id,q=!q||q===c||q.length<1||i[q]?f.nextid++:i[q]=q,o=".qtip-"+q+"-create",p=y.call(this,q,d);if(p===c)return b;l=p.options,a.each(g,function(){this.initialize==="initialize"&&this(p)}),m={show:l.show.target,hide:l.hide.target},n={show:a.trim(""+l.show.event).replace(/ /g,o+" ")+o,hide:a.trim(""+l.hide.event).replace(/ /g,o+" ")+o},/mouse(over|enter)/i.test(n.show)&&!/mouse(out|leave)/i.test(n.hide)&&(n.hide+=" mouseleave"+o),m.show.bind("mousemove"+o,function(a){h={pageX:a.pageX,pageY:a.pageY,type:"mousemove"},p.cache.onTarget=b}),m.show.bind(n.show,r),(l.show.ready||l.prerender)&&r(j)})},g=f.plugins={Corner:function(a){a=(""+a).replace(/([A-Z])/," $1").replace(/middle/gi,"center").toLowerCase(),this.x=(a.match(/left|right/i)||a.match(/center/)||["inherit"])[0].toLowerCase(),this.y=(a.match(/top|bottom|center/i)||["inherit"])[0].toLowerCase();var b=a.charAt(0);this.precedance=b==="t"||b==="b"?"y":"x",this.string=function(){return this.precedance==="y"?this.y+this.x:this.x+this.y},this.abbrev=function(){var a=this.x.substr(0,1),b=this.y.substr(0,1);return a===b?a:a==="c"||a!=="c"&&b!=="c"?b+a:a+b},this.clone=function(){return{x:this.x,y:this.y,precedance:this.precedance,string:this.string,abbrev:this.abbrev,clone:this.clone}}},offset:function(b,c){function j(a,b){d.left+=b*a.scrollLeft(),d.top+=b*a.scrollTop()}var d=b.offset(),e=b.closest("body")[0],f=c,g,h,i;if(f){do f.css("position")!=="static"&&(h=f.position(),d.left-=h.left+(parseInt(f.css("borderLeftWidth"),10)||0)+(parseInt(f.css("marginLeft"),10)||0),d.top-=h.top+(parseInt(f.css("borderTopWidth"),10)||0)+(parseInt(f.css("marginTop"),10)||0),!g&&(i=f.css("overflow"))!=="hidden"&&i!=="visible"&&(g=f));while((f=a(f[0].offsetParent)).length);g&&g[0]!==e&&j(g,1)}return d},iOS:parseFloat((""+(/CPU.*OS ([0-9_]{1,3})|(CPU like).*AppleWebKit.*Mobile/i.exec(navigator.userAgent)||[0,""])[1]).replace("undefined","3_2").replace("_","."))||c,fn:{attr:function(b,c){if(this.length){var d=this[0],e="title",f=a.data(d,"qtip");if(b===e&&f&&"object"===typeof f&&f.options.suppress){if(arguments.length<2)return a.attr(d,t);f&&f.options.content.attr===e&&f.cache.attr&&f.set("content.text",c);return this.attr(t,c)}}return a.fn["attr"+s].apply(this,arguments)},clone:function(b){var c=a([]),d="title",e=a.fn["clone"+s].apply(this,arguments);b||e.filter("["+t+"]").attr("title",function(){return a.attr(this,t)}).removeAttr(t);return e}}},a.each(g.fn,function(c,d){if(!d||a.fn[c+s])return b;var e=a.fn[c+s]=a.fn[c];a.fn[c]=function(){return d.apply(this,arguments)||e.apply(this,arguments)}}),a.ui||(a["cleanData"+s]=a.cleanData,a.cleanData=function(b){for(var c=0,d;(d=b[c])!==e;c++)try{a(d).triggerHandler("removeqtip")}catch(f){}a["cleanData"+s](b)}),f.version="nightly",f.nextid=0,f.inactiveEvents="click dblclick mousedown mouseup mousemove mouseleave mouseenter".split(" "),f.zindex=15e3,f.defaults={prerender:c,id:c,overwrite:b,suppress:b,content:{text:b,attr:"title",title:{text:c,button:c}},position:{my:"top left",at:"bottom right",target:c,container:c,viewport:c,adjust:{x:0,y:0,mouse:b,resize:b,method:"flip flip"},effect:function(b,d,e){a(this).animate(d,{duration:200,queue:c})}},show:{target:c,event:"mouseenter",effect:b,delay:90,solo:c,ready:c,autofocus:c},hide:{target:c,event:"mouseleave",effect:b,delay:0,fixed:c,inactive:c,leave:"window",distance:c},style:{classes:"",widget:c,width:c,height:c,def:b},events:{render:d,move:d,show:d,hide:d,toggle:d,visible:d,hidden:d,focus:d,blur:d}},g.ajax=function(a){var b=a.plugins.ajax;return"object"===typeof b?b:a.plugins.ajax=new z(a)},g.ajax.initialize="render",g.ajax.sanitize=function(a){var b=a.content,c;b&&"ajax"in b&&(c=b.ajax,typeof c!=="object"&&(c=a.content.ajax={url:c}),"boolean"!==typeof c.once&&c.once&&(c.once=!!c.once))},a.extend(b,f.defaults,{content:{ajax:{loading:b,once:b}}})}) \ No newline at end of file diff --git a/templates/main.html b/templates/main.html index 07e1d75695..1d30ee6167 100644 --- a/templates/main.html +++ b/templates/main.html @@ -107,6 +107,7 @@ + diff --git a/templates/sass/application.scss b/templates/sass/application.scss index a5377b03fd..c493202e33 100644 --- a/templates/sass/application.scss +++ b/templates/sass/application.scss @@ -4,7 +4,7 @@ @import "base/reset", "base/font-face"; @import "base/variables", "base/functions", "base/extends", "base/base"; @import "layout/layout", "layout/header", "layout/footer", "layout/calculator", "layout/leanmodal"; -@import "plugins/jquery-ui-1.8.16.custom"; +@import "plugins/jquery-ui-1.8.16.custom", "plugins/jquery.qtip.min"; // pages @import "courseware/courseware", "courseware/sidebar", "courseware/video", "courseware/sequence-nav", "courseware/amplifier"; diff --git a/templates/sass/courseware/_video.scss b/templates/sass/courseware/_video.scss index 1ec241dda1..46e6fd8889 100644 --- a/templates/sass/courseware/_video.scss +++ b/templates/sass/courseware/_video.scss @@ -62,6 +62,7 @@ section.course-content { background: #333; position: relative; border: 1px solid #000; + border-top: 0; color: #ccc; div#slider { @@ -70,10 +71,16 @@ section.course-content { @include box-shadow(inset 0 1px 0 #eee, 0 1px 0 #555); background: #c2c2c2; border: none; + border-top: 1px solid #000; border-bottom: 1px solid #000; height: 7px; @include transition(); + div.ui-widget-header { + background: #777; + @include box-shadow(inset 0 1px 0 #999); + } + a.ui-slider-handle { @include border-radius(15px); @include box-shadow(inset 0 1px 0 lighten($mit-red, 10%)); @@ -125,6 +132,7 @@ section.course-content { padding: lh(.75); text-indent: -9999px; width: 14px; + @include transition(); &.play { background: url('/static/images/play-icon.png') center center no-repeat; @@ -166,6 +174,7 @@ section.course-content { position: relative; @include transition(); width: 110px; + cursor: pointer; -webkit-font-smoothing: antialiased; h3 { diff --git a/templates/sass/plugins/_jquery.qtip.min.scss b/templates/sass/plugins/_jquery.qtip.min.scss new file mode 100644 index 0000000000..e6b3aab2e6 --- /dev/null +++ b/templates/sass/plugins/_jquery.qtip.min.scss @@ -0,0 +1 @@ +.ui-tooltip,.qtip{position:absolute;left:-28000px;top:-28000px;display:none;max-width:280px;min-width:50px;font-size:10.5px;line-height:12px;}.ui-tooltip-fluid{display:block;visibility:hidden;position:static!important;float:left!important;}.ui-tooltip-content{position:relative;padding:5px 9px;overflow:hidden;border:1px solid #000001;text-align:left;word-wrap:break-word;overflow:hidden;}.ui-tooltip-titlebar{position:relative;min-height:14px;padding:5px 35px 5px 10px;overflow:hidden;border:1px solid #000001;border-width:1px 1px 0;font-weight:bold;}.ui-tooltip-titlebar+.ui-tooltip-content{border-top-width:0!important;}/*!Default close button class */ .ui-tooltip-titlebar .ui-state-default{position:absolute;right:4px;top:50%;margin-top:-9px;cursor:pointer;outline:medium none;border-width:1px;border-style:solid;}* html .ui-tooltip-titlebar .ui-state-default{top:16px;}.ui-tooltip-titlebar .ui-icon,.ui-tooltip-icon .ui-icon{display:block;text-indent:-1000em;}.ui-tooltip-icon,.ui-tooltip-icon .ui-icon{-moz-border-radius:3px;-webkit-border-radius:3px;border-radius:3px;}.ui-tooltip-icon .ui-icon{width:18px;height:14px;text-align:center;text-indent:0;font:normal bold 10px/13px Tahoma,sans-serif;color:inherit;background:transparent none no-repeat -100em -100em;}/*!Default tooltip style */ .ui-tooltip-default .ui-tooltip-titlebar,.ui-tooltip-default .ui-tooltip-content{border-color:#F1D031;background-color:#FFFFA3;color:#555;}.ui-tooltip-default .ui-tooltip-titlebar{background-color:#FFEF93;}.ui-tooltip-default .ui-tooltip-icon{border-color:#CCC;background:#F1F1F1;color:#777;}.ui-tooltip-default .ui-tooltip-titlebar .ui-state-hover{border-color:#AAA;color:#111;} \ No newline at end of file diff --git a/templates/video_init.js b/templates/video_init.js index c9fe437888..e483ec65fe 100644 --- a/templates/video_init.js +++ b/templates/video_init.js @@ -20,9 +20,38 @@ if (swfobject.hasFlashPlayerVersion("10.1")){ // Make sure the callback is called once API ready, YT seems to be buggy loadHTML5Video(); } + var captions=0; -$("#slider").slider({slide:function(event,ui){seek_slide('slide',event.originalEvent,ui.value);}, - stop:function(event,ui){seek_slide('stop',event.originalEvent,ui.value);}}); + +/* Cache a reference to our slider element */ +var slider = $('#slider') + +.slider({range: "min", slide:function(event,ui){seek_slide('slide',event.originalEvent,ui.value); handle.qtip('option', 'content.text', '' + ui.value);}, stop:function(event,ui){seek_slide('stop',event.originalEvent,ui.value);}}), + +/* Grab and cache the newly created slider handle */ +handle = $('.ui-slider-handle', slider); + +/* + * Selector needs changing here to match your elements. + * + * Notice the second argument to the $() constructor, which tells + * jQuery to use that as the top-level element to seareh down from. + */ + handle.qtip({ + content: '' + slider.slider('option', 'value'), // Use the current value of the slider + position: { + my: 'bottom center', + at: 'top center', + container: handle // Stick it inside the handle element so it keeps the position synched up + }, + hide: { + delay: 700 // Give it a longer delay so it doesn't hide frequently as we move the handle + }, + style: { + classes: 'ui-tooltip-slider', + widget: true // Make it Themeroller compatible + } + }); function good() { window['console'].log(ytplayer.getCurrentTime()); @@ -92,8 +121,6 @@ $(document).ready(function() { }); - - function toggleVideo(){ if ($("#video_control").hasClass("play")){ play(); From 032f5fc928a9ea6810069b87716be0a2b06ff64a Mon Sep 17 00:00:00 2001 From: Kyle Fiedler Date: Tue, 3 Apr 2012 14:30:00 -0400 Subject: [PATCH 35/50] Added style for the tooltip --- static/css/application.css | 36 +++++++++++++++++++++++++++ templates/sass/courseware/_video.scss | 30 ++++++++++++++++++++++ 2 files changed, 66 insertions(+) diff --git a/static/css/application.css b/static/css/application.css index 869571bdf2..0c088fe6a4 100644 --- a/static/css/application.css +++ b/static/css/application.css @@ -2945,6 +2945,42 @@ section.course-content div.video-subtitles div.video-wrapper section.video-contr -webkit-box-shadow: inset 0 1px 0 #999999; -moz-box-shadow: inset 0 1px 0 #999999; box-shadow: inset 0 1px 0 #999999; } +section.course-content div.video-subtitles div.video-wrapper section.video-controls div#slider .ui-tooltip.qtip .ui-tooltip-content { + background: #993333; + border: 1px solid #4d1919; + -webkit-border-radius: 2px; + -moz-border-radius: 2px; + -ms-border-radius: 2px; + -o-border-radius: 2px; + border-radius: 2px; + -webkit-box-shadow: inset 0 1px 0 #bf4040; + -moz-box-shadow: inset 0 1px 0 #bf4040; + box-shadow: inset 0 1px 0 #bf4040; + color: #fff; + font: bold 12px "Open Sans", "Lucida Grande", "Lucida Sans Unicode", "Lucida Sans", Geneva, Verdana, sans-serif; + margin-bottom: 6px; + padding: 4px; + text-align: center; + -webkit-font-smoothing: antialiased; + text-shadow: 0 -1px 0 #732626; + overflow: visible; } +section.course-content div.video-subtitles div.video-wrapper section.video-controls div#slider .ui-tooltip.qtip .ui-tooltip-content::after { + content: " "; + width: 7px; + height: 7px; + display: block; + position: absolute; + bottom: -5px; + left: 50%; + margin-left: -3px; + -webkit-transform: rotate(45deg); + -moz-transform: rotate(45deg); + -ms-transform: rotate(45deg); + -o-transform: rotate(45deg); + transform: rotate(45deg); + background: #993333; + border-right: 1px solid #4d1919; + border-bottom: 1px solid #4d1919; } section.course-content div.video-subtitles div.video-wrapper section.video-controls div#slider a.ui-slider-handle { -webkit-border-radius: 15px; -moz-border-radius: 15px; diff --git a/templates/sass/courseware/_video.scss b/templates/sass/courseware/_video.scss index 46e6fd8889..cec9ec1cad 100644 --- a/templates/sass/courseware/_video.scss +++ b/templates/sass/courseware/_video.scss @@ -81,6 +81,36 @@ section.course-content { @include box-shadow(inset 0 1px 0 #999); } + .ui-tooltip.qtip .ui-tooltip-content { + background: $mit-red; + border: 1px solid darken($mit-red, 20%); + @include border-radius(2px); + @include box-shadow(inset 0 1px 0 lighten($mit-red, 10%)); + color: #fff; + font: bold 12px $body-font-family; + margin-bottom: 6px; + padding: 4px; + text-align: center; + -webkit-font-smoothing: antialiased; + text-shadow: 0 -1px 0 darken($mit-red, 10%); + overflow: visible; + + &::after { + content: " "; + width: 7px; + height: 7px; + display: block; + position: absolute; + bottom: -5px; + left: 50%; + margin-left: -3px; + @include transform(rotate(45deg)); + background: $mit-red; + border-right: 1px solid darken($mit-red, 20%); + border-bottom: 1px solid darken($mit-red, 20%); + } + } + a.ui-slider-handle { @include border-radius(15px); @include box-shadow(inset 0 1px 0 lighten($mit-red, 10%)); From 4225317dad98d4e7685c08263537eec6e8e92252 Mon Sep 17 00:00:00 2001 From: Kyle Fiedler Date: Wed, 4 Apr 2012 10:26:07 -0400 Subject: [PATCH 36/50] Added some js to make the tooltip show the time --- static/js/video_player.js | 2 ++ templates/video_init.js | 11 ++++++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/static/js/video_player.js b/static/js/video_player.js index f539072659..1d5fd9b6e3 100644 --- a/static/js/video_player.js +++ b/static/js/video_player.js @@ -352,6 +352,8 @@ function updateytplayerInfo() { } if (player_state == 1){ update_captions(getCurrentTime()); + handle = $('.ui-slider-handle', slider); + handle.qtip('option', 'content.text', '' + format_time(getCurrentTime())); } // updateHTML("videoduration", getDuration()); // updateHTML("videotime", getCurrentTime()); diff --git a/templates/video_init.js b/templates/video_init.js index e483ec65fe..a94837b536 100644 --- a/templates/video_init.js +++ b/templates/video_init.js @@ -26,7 +26,16 @@ var captions=0; /* Cache a reference to our slider element */ var slider = $('#slider') -.slider({range: "min", slide:function(event,ui){seek_slide('slide',event.originalEvent,ui.value); handle.qtip('option', 'content.text', '' + ui.value);}, stop:function(event,ui){seek_slide('stop',event.originalEvent,ui.value);}}), +.slider({ + range: "min", + slide: function(event,ui) { + var time = format_time(ui.value) + + seek_slide('slide',event.originalEvent,ui.value); + handle.qtip('option', 'content.text', '' + time); + }, + stop:function(event,ui){seek_slide('stop',event.originalEvent,ui.value);} +}), /* Grab and cache the newly created slider handle */ handle = $('.ui-slider-handle', slider); From 7c2cb55d8132d96765401cfb44fc38194d722396 Mon Sep 17 00:00:00 2001 From: Kyle Fiedler Date: Wed, 4 Apr 2012 11:43:26 -0400 Subject: [PATCH 37/50] Added some firefox fixes --- static/js/video_player.js | 4 ++-- templates/video_init.js | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/static/js/video_player.js b/static/js/video_player.js index 1d5fd9b6e3..5b3b9377a9 100644 --- a/static/js/video_player.js +++ b/static/js/video_player.js @@ -352,7 +352,7 @@ function updateytplayerInfo() { } if (player_state == 1){ update_captions(getCurrentTime()); - handle = $('.ui-slider-handle', slider); + handle = $('.ui-slider-handle', $('#slider')); handle.qtip('option', 'content.text', '' + format_time(getCurrentTime())); } // updateHTML("videoduration", getDuration()); @@ -422,7 +422,7 @@ function stop() { function getPlayerState() { if (ytplayer) { - return ytplayer.getPlayerState(); + return ytplayer.getPlayerState(); } } diff --git a/templates/video_init.js b/templates/video_init.js index a94837b536..040e944a0c 100644 --- a/templates/video_init.js +++ b/templates/video_init.js @@ -29,10 +29,10 @@ var slider = $('#slider') .slider({ range: "min", slide: function(event,ui) { - var time = format_time(ui.value) + var slider_time = format_time(ui.value) seek_slide('slide',event.originalEvent,ui.value); - handle.qtip('option', 'content.text', '' + time); + handle.qtip('option', 'content.text', '' + slider_time); }, stop:function(event,ui){seek_slide('stop',event.originalEvent,ui.value);} }), From 34627fb0c905673ce998d3fea06043325308ee99 Mon Sep 17 00:00:00 2001 From: Kyle Fiedler Date: Wed, 4 Apr 2012 12:20:28 -0400 Subject: [PATCH 38/50] Speed up dropdown animation --- templates/video.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/video.html b/templates/video.html index bb7d1b9ed0..39a72d02a6 100644 --- a/templates/video.html +++ b/templates/video.html @@ -73,7 +73,7 @@ $("ol#video_speeds").hide(); $("div.speeds").click(function() { - $("ol#video_speeds").slideToggle(); + $("ol#video_speeds").slideToggle(150); }); }); From d4a462bd74f2547c4a7d2bc62f22ebafd9f71db9 Mon Sep 17 00:00:00 2001 From: Kyle Fiedler Date: Wed, 4 Apr 2012 14:23:10 -0400 Subject: [PATCH 39/50] only transform height and witdh --- static/css/application.css | 30 +++++++++++++-------------- templates/sass/courseware/_video.scss | 4 ++-- templates/video_init.js | 2 +- 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/static/css/application.css b/static/css/application.css index 0c088fe6a4..22cb5c9a55 100644 --- a/static/css/application.css +++ b/static/css/application.css @@ -2920,11 +2920,11 @@ section.course-content div.video-subtitles div.video-wrapper section.video-contr border-top: 1px solid #000; border-bottom: 1px solid #000; height: 7px; - -webkit-transition-property: all; - -moz-transition-property: all; - -ms-transition-property: all; - -o-transition-property: all; - transition-property: all; + -webkit-transition-property: height, 2s, ease-in-out; + -moz-transition-property: height, 2s, ease-in-out; + -ms-transition-property: height, 2s, ease-in-out; + -o-transition-property: height, 2s, ease-in-out; + transition-property: height, 2s, ease-in-out; -webkit-transition-duration: 0.15s; -moz-transition-duration: 0.15s; -ms-transition-duration: 0.15s; @@ -2997,16 +2997,16 @@ section.course-content div.video-subtitles div.video-wrapper section.video-contr margin-left: -7px; top: -4px; width: 15px; - -webkit-transition-property: all; - -moz-transition-property: all; - -ms-transition-property: all; - -o-transition-property: all; - transition-property: all; - -webkit-transition-duration: 0.15s; - -moz-transition-duration: 0.15s; - -ms-transition-duration: 0.15s; - -o-transition-duration: 0.15s; - transition-duration: 0.15s; + -webkit-transition-property: height, 2s, ease-in-out; + -moz-transition-property: height, 2s, ease-in-out; + -ms-transition-property: height, 2s, ease-in-out; + -o-transition-property: height, 2s, ease-in-out; + transition-property: height, 2s, ease-in-out; + -webkit-transition-duration: width, 2s, ease-in-out; + -moz-transition-duration: width, 2s, ease-in-out; + -ms-transition-duration: width, 2s, ease-in-out; + -o-transition-duration: width, 2s, ease-in-out; + transition-duration: width, 2s, ease-in-out; -webkit-transition-timing-function: ease-out; -moz-transition-timing-function: ease-out; -ms-transition-timing-function: ease-out; diff --git a/templates/sass/courseware/_video.scss b/templates/sass/courseware/_video.scss index cec9ec1cad..03597b4fa7 100644 --- a/templates/sass/courseware/_video.scss +++ b/templates/sass/courseware/_video.scss @@ -74,7 +74,7 @@ section.course-content { border-top: 1px solid #000; border-bottom: 1px solid #000; height: 7px; - @include transition(); + @include transition(height 2.0s ease-in-out); div.ui-widget-header { background: #777; @@ -121,7 +121,7 @@ section.course-content { margin-left: -7px; top: -4px; width: 15px; - @include transition(); + @include transition(height 2.0s ease-in-out, width 2.0s ease-in-out); @include background-size(50%); &:focus, &:hover { diff --git a/templates/video_init.js b/templates/video_init.js index 040e944a0c..ea72242539 100644 --- a/templates/video_init.js +++ b/templates/video_init.js @@ -27,7 +27,7 @@ var captions=0; var slider = $('#slider') .slider({ - range: "min", + range: "min", slide: function(event,ui) { var slider_time = format_time(ui.value) From a1224bd7b954a6791e01dacf94aaedb93589a352 Mon Sep 17 00:00:00 2001 From: Calen Pennington Date: Thu, 5 Apr 2012 13:30:42 -0400 Subject: [PATCH 40/50] Mark that the mitx package provides /opt/wwc/mitx --- rakefile | 1 + 1 file changed, 1 insertion(+) diff --git a/rakefile b/rakefile index 414bb77c5d..1838b019fb 100644 --- a/rakefile +++ b/rakefile @@ -65,6 +65,7 @@ task :package do "--depends=python-markdown", "--depends=python-pygments", "--depends=mysql-client", + "--provides=#{LINK_PATH}", "--name=#{DEPLOY_NAME}", "--version=#{VERSION}", "-a", "all", From 442d0691a5586ec89d09ecc5f0afb08f81f9f842 Mon Sep 17 00:00:00 2001 From: Calen Pennington Date: Thu, 5 Apr 2012 14:03:27 -0400 Subject: [PATCH 41/50] Use the base package name as the provided value, rather than the path, because .deb packages can't start with / --- rakefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rakefile b/rakefile index 1838b019fb..f99ca9b067 100644 --- a/rakefile +++ b/rakefile @@ -65,7 +65,7 @@ task :package do "--depends=python-markdown", "--depends=python-pygments", "--depends=mysql-client", - "--provides=#{LINK_PATH}", + "--provides=#{PACKAGE_NAME}", "--name=#{DEPLOY_NAME}", "--version=#{VERSION}", "-a", "all", From 78f83925994d29a8b741a6f43f23ec26c8e7090d Mon Sep 17 00:00:00 2001 From: Kyle Fiedler Date: Wed, 21 Mar 2012 11:03:48 -0400 Subject: [PATCH 42/50] added styles from old handouts --- static/css/application.css | 62 ++++++++++++++++++++++++++++---------- templates/sass/_info.scss | 60 +++++++++++++++++++++++++----------- 2 files changed, 89 insertions(+), 33 deletions(-) diff --git a/static/css/application.css b/static/css/application.css index 22cb5c9a55..0187781730 100644 --- a/static/css/application.css +++ b/static/css/application.css @@ -262,7 +262,7 @@ h1.top-header, div.course-wrapper section.course-content h1, div.info-wrapper se .sidebar a, section.course-index a, div.book-wrapper section.book-sidebar a, div.info-wrapper section.handouts a, div.profile-wrapper section.user-info a, div#wiki_panel a, div.discussion-wrapper aside a { font-style: normal; border: none; } -.sidebar .bottom-border, section.course-index .bottom-border, div.book-wrapper section.book-sidebar .bottom-border, div.info-wrapper section.handouts .bottom-border, div.profile-wrapper section.user-info .bottom-border, div#wiki_panel .bottom-border, div.discussion-wrapper aside .bottom-border, .sidebar h3, section.course-index .sidebar h3, .sidebar section.course-index h3, div.book-wrapper section.book-sidebar .sidebar h3, .sidebar div.book-wrapper section.book-sidebar h3, div.info-wrapper section.handouts .sidebar h3, .sidebar div.info-wrapper section.handouts h3, div.profile-wrapper section.user-info .sidebar h3, .sidebar div.profile-wrapper section.user-info h3, div#wiki_panel .sidebar h3, .sidebar div#wiki_panel h3, div.discussion-wrapper aside .sidebar h3, .sidebar div.discussion-wrapper aside h3, .sidebar section.course-index h3, section.course-index .sidebar h3, section.course-index h3, div.book-wrapper section.book-sidebar section.course-index h3, section.course-index div.book-wrapper section.book-sidebar h3, div.info-wrapper section.handouts section.course-index h3, section.course-index div.info-wrapper section.handouts h3, div.profile-wrapper section.user-info section.course-index h3, section.course-index div.profile-wrapper section.user-info h3, div#wiki_panel section.course-index h3, section.course-index div#wiki_panel h3, div.discussion-wrapper aside section.course-index h3, section.course-index div.discussion-wrapper aside h3, .sidebar div.book-wrapper section.book-sidebar h3, div.book-wrapper section.book-sidebar .sidebar h3, section.course-index div.book-wrapper section.book-sidebar h3, div.book-wrapper section.book-sidebar section.course-index h3, div.book-wrapper section.book-sidebar h3, div.info-wrapper section.handouts div.book-wrapper section.book-sidebar h3, div.book-wrapper section.book-sidebar div.info-wrapper section.handouts h3, div.profile-wrapper section.user-info div.book-wrapper section.book-sidebar h3, div.book-wrapper section.book-sidebar div.profile-wrapper section.user-info h3, div#wiki_panel div.book-wrapper section.book-sidebar h3, div.book-wrapper section.book-sidebar div#wiki_panel h3, div.discussion-wrapper aside div.book-wrapper section.book-sidebar h3, div.book-wrapper section.book-sidebar div.discussion-wrapper aside h3, .sidebar div.info-wrapper section.handouts h3, div.info-wrapper section.handouts .sidebar h3, section.course-index div.info-wrapper section.handouts h3, div.info-wrapper section.handouts section.course-index h3, div.book-wrapper section.book-sidebar div.info-wrapper section.handouts h3, div.info-wrapper section.handouts div.book-wrapper section.book-sidebar h3, div.info-wrapper section.handouts h3, div.profile-wrapper section.user-info div.info-wrapper section.handouts h3, div.info-wrapper section.handouts div.profile-wrapper section.user-info h3, div#wiki_panel div.info-wrapper section.handouts h3, div.info-wrapper section.handouts div#wiki_panel h3, div.discussion-wrapper aside div.info-wrapper section.handouts h3, div.info-wrapper section.handouts div.discussion-wrapper aside h3, .sidebar div.profile-wrapper section.user-info h3, div.profile-wrapper section.user-info .sidebar h3, section.course-index div.profile-wrapper section.user-info h3, div.profile-wrapper section.user-info section.course-index h3, div.book-wrapper section.book-sidebar div.profile-wrapper section.user-info h3, div.profile-wrapper section.user-info div.book-wrapper section.book-sidebar h3, div.info-wrapper section.handouts div.profile-wrapper section.user-info h3, div.profile-wrapper section.user-info div.info-wrapper section.handouts h3, div.profile-wrapper section.user-info h3, div#wiki_panel div.profile-wrapper section.user-info h3, div.profile-wrapper section.user-info div#wiki_panel h3, div.discussion-wrapper aside div.profile-wrapper section.user-info h3, div.profile-wrapper section.user-info div.discussion-wrapper aside h3, .sidebar div#wiki_panel h3, div#wiki_panel .sidebar h3, section.course-index div#wiki_panel h3, div#wiki_panel section.course-index h3, div.book-wrapper section.book-sidebar div#wiki_panel h3, div#wiki_panel div.book-wrapper section.book-sidebar h3, div.info-wrapper section.handouts div#wiki_panel h3, div#wiki_panel div.info-wrapper section.handouts h3, div.profile-wrapper section.user-info div#wiki_panel h3, div#wiki_panel div.profile-wrapper section.user-info h3, div#wiki_panel h3, div.discussion-wrapper aside div#wiki_panel h3, div#wiki_panel div.discussion-wrapper aside h3, .sidebar div.discussion-wrapper aside h3, div.discussion-wrapper aside .sidebar h3, section.course-index div.discussion-wrapper aside h3, div.discussion-wrapper aside section.course-index h3, div.book-wrapper section.book-sidebar div.discussion-wrapper aside h3, div.discussion-wrapper aside div.book-wrapper section.book-sidebar h3, div.info-wrapper section.handouts div.discussion-wrapper aside h3, div.discussion-wrapper aside div.info-wrapper section.handouts h3, div.profile-wrapper section.user-info div.discussion-wrapper aside h3, div.discussion-wrapper aside div.profile-wrapper section.user-info h3, div#wiki_panel div.discussion-wrapper aside h3, div.discussion-wrapper aside div#wiki_panel h3, div.discussion-wrapper aside h3, .sidebar div#wiki_panel input[type="button"], section.course-index .sidebar div#wiki_panel input[type="button"], .sidebar div#wiki_panel section.course-index input[type="button"], div.book-wrapper section.book-sidebar .sidebar div#wiki_panel input[type="button"], .sidebar div#wiki_panel div.book-wrapper section.book-sidebar input[type="button"], div.info-wrapper section.handouts .sidebar div#wiki_panel input[type="button"], .sidebar div#wiki_panel div.info-wrapper section.handouts input[type="button"], div.profile-wrapper section.user-info .sidebar div#wiki_panel input[type="button"], .sidebar div#wiki_panel div.profile-wrapper section.user-info input[type="button"], .sidebar div#wiki_panel input[type="button"], div.discussion-wrapper aside .sidebar div#wiki_panel input[type="button"], .sidebar div#wiki_panel div.discussion-wrapper aside input[type="button"], div#wiki_panel .sidebar input[type="button"], section.course-index div#wiki_panel .sidebar input[type="button"], div#wiki_panel .sidebar section.course-index input[type="button"], div.book-wrapper section.book-sidebar div#wiki_panel .sidebar input[type="button"], div#wiki_panel .sidebar div.book-wrapper section.book-sidebar input[type="button"], div.info-wrapper section.handouts div#wiki_panel .sidebar input[type="button"], div#wiki_panel .sidebar div.info-wrapper section.handouts input[type="button"], div.profile-wrapper section.user-info div#wiki_panel .sidebar input[type="button"], div#wiki_panel .sidebar div.profile-wrapper section.user-info input[type="button"], div#wiki_panel .sidebar input[type="button"], div.discussion-wrapper aside div#wiki_panel .sidebar input[type="button"], div#wiki_panel .sidebar div.discussion-wrapper aside input[type="button"], .sidebar section.course-index div#wiki_panel input[type="button"], section.course-index div#wiki_panel .sidebar input[type="button"], section.course-index div#wiki_panel input[type="button"], div.book-wrapper section.book-sidebar section.course-index div#wiki_panel input[type="button"], section.course-index div#wiki_panel div.book-wrapper section.book-sidebar input[type="button"], div.info-wrapper section.handouts section.course-index div#wiki_panel input[type="button"], section.course-index div#wiki_panel div.info-wrapper section.handouts input[type="button"], div.profile-wrapper section.user-info section.course-index div#wiki_panel input[type="button"], section.course-index div#wiki_panel div.profile-wrapper section.user-info input[type="button"], section.course-index div#wiki_panel input[type="button"], div.discussion-wrapper aside section.course-index div#wiki_panel input[type="button"], section.course-index div#wiki_panel div.discussion-wrapper aside input[type="button"], .sidebar div#wiki_panel section.course-index input[type="button"], div#wiki_panel section.course-index .sidebar input[type="button"], div#wiki_panel section.course-index input[type="button"], div.book-wrapper section.book-sidebar div#wiki_panel section.course-index input[type="button"], div#wiki_panel section.course-index div.book-wrapper section.book-sidebar input[type="button"], div.info-wrapper section.handouts div#wiki_panel section.course-index input[type="button"], div#wiki_panel section.course-index div.info-wrapper section.handouts input[type="button"], div.profile-wrapper section.user-info div#wiki_panel section.course-index input[type="button"], div#wiki_panel section.course-index div.profile-wrapper section.user-info input[type="button"], div#wiki_panel section.course-index input[type="button"], div.discussion-wrapper aside div#wiki_panel section.course-index input[type="button"], div#wiki_panel section.course-index div.discussion-wrapper aside input[type="button"], .sidebar div.book-wrapper section.book-sidebar div#wiki_panel input[type="button"], div.book-wrapper section.book-sidebar div#wiki_panel .sidebar input[type="button"], section.course-index div.book-wrapper section.book-sidebar div#wiki_panel input[type="button"], div.book-wrapper section.book-sidebar div#wiki_panel section.course-index input[type="button"], div.book-wrapper section.book-sidebar div#wiki_panel input[type="button"], div.info-wrapper section.handouts div.book-wrapper section.book-sidebar div#wiki_panel input[type="button"], div.book-wrapper section.book-sidebar div#wiki_panel div.info-wrapper section.handouts input[type="button"], div.profile-wrapper section.user-info div.book-wrapper section.book-sidebar div#wiki_panel input[type="button"], div.book-wrapper section.book-sidebar div#wiki_panel div.profile-wrapper section.user-info input[type="button"], div.book-wrapper section.book-sidebar div#wiki_panel input[type="button"], div.discussion-wrapper aside div.book-wrapper section.book-sidebar div#wiki_panel input[type="button"], div.book-wrapper section.book-sidebar div#wiki_panel div.discussion-wrapper aside input[type="button"], .sidebar div#wiki_panel div.book-wrapper section.book-sidebar input[type="button"], div#wiki_panel div.book-wrapper section.book-sidebar .sidebar input[type="button"], section.course-index div#wiki_panel div.book-wrapper section.book-sidebar input[type="button"], div#wiki_panel div.book-wrapper section.book-sidebar section.course-index input[type="button"], div#wiki_panel div.book-wrapper section.book-sidebar input[type="button"], div.info-wrapper section.handouts div#wiki_panel div.book-wrapper section.book-sidebar input[type="button"], div#wiki_panel div.book-wrapper section.book-sidebar div.info-wrapper section.handouts input[type="button"], div.profile-wrapper section.user-info div#wiki_panel div.book-wrapper section.book-sidebar input[type="button"], div#wiki_panel div.book-wrapper section.book-sidebar div.profile-wrapper section.user-info input[type="button"], div#wiki_panel div.book-wrapper section.book-sidebar input[type="button"], div.discussion-wrapper aside div#wiki_panel div.book-wrapper section.book-sidebar input[type="button"], div#wiki_panel div.book-wrapper section.book-sidebar div.discussion-wrapper aside input[type="button"], .sidebar div.info-wrapper section.handouts div#wiki_panel input[type="button"], div.info-wrapper section.handouts div#wiki_panel .sidebar input[type="button"], section.course-index div.info-wrapper section.handouts div#wiki_panel input[type="button"], div.info-wrapper section.handouts div#wiki_panel section.course-index input[type="button"], div.book-wrapper section.book-sidebar div.info-wrapper section.handouts div#wiki_panel input[type="button"], div.info-wrapper section.handouts div#wiki_panel div.book-wrapper section.book-sidebar input[type="button"], div.info-wrapper section.handouts div#wiki_panel input[type="button"], div.profile-wrapper section.user-info div.info-wrapper section.handouts div#wiki_panel input[type="button"], div.info-wrapper section.handouts div#wiki_panel div.profile-wrapper section.user-info input[type="button"], div.info-wrapper section.handouts div#wiki_panel input[type="button"], div.discussion-wrapper aside div.info-wrapper section.handouts div#wiki_panel input[type="button"], div.info-wrapper section.handouts div#wiki_panel div.discussion-wrapper aside input[type="button"], .sidebar div#wiki_panel div.info-wrapper section.handouts input[type="button"], div#wiki_panel div.info-wrapper section.handouts .sidebar input[type="button"], section.course-index div#wiki_panel div.info-wrapper section.handouts input[type="button"], div#wiki_panel div.info-wrapper section.handouts section.course-index input[type="button"], div.book-wrapper section.book-sidebar div#wiki_panel div.info-wrapper section.handouts input[type="button"], div#wiki_panel div.info-wrapper section.handouts div.book-wrapper section.book-sidebar input[type="button"], div#wiki_panel div.info-wrapper section.handouts input[type="button"], div.profile-wrapper section.user-info div#wiki_panel div.info-wrapper section.handouts input[type="button"], div#wiki_panel div.info-wrapper section.handouts div.profile-wrapper section.user-info input[type="button"], div#wiki_panel div.info-wrapper section.handouts input[type="button"], div.discussion-wrapper aside div#wiki_panel div.info-wrapper section.handouts input[type="button"], div#wiki_panel div.info-wrapper section.handouts div.discussion-wrapper aside input[type="button"], .sidebar div.profile-wrapper section.user-info div#wiki_panel input[type="button"], div.profile-wrapper section.user-info div#wiki_panel .sidebar input[type="button"], section.course-index div.profile-wrapper section.user-info div#wiki_panel input[type="button"], div.profile-wrapper section.user-info div#wiki_panel section.course-index input[type="button"], div.book-wrapper section.book-sidebar div.profile-wrapper section.user-info div#wiki_panel input[type="button"], div.profile-wrapper section.user-info div#wiki_panel div.book-wrapper section.book-sidebar input[type="button"], div.info-wrapper section.handouts div.profile-wrapper section.user-info div#wiki_panel input[type="button"], div.profile-wrapper section.user-info div#wiki_panel div.info-wrapper section.handouts input[type="button"], div.profile-wrapper section.user-info div#wiki_panel input[type="button"], div.profile-wrapper section.user-info div#wiki_panel input[type="button"], div.discussion-wrapper aside div.profile-wrapper section.user-info div#wiki_panel input[type="button"], div.profile-wrapper section.user-info div#wiki_panel div.discussion-wrapper aside input[type="button"], .sidebar div#wiki_panel div.profile-wrapper section.user-info input[type="button"], div#wiki_panel div.profile-wrapper section.user-info .sidebar input[type="button"], section.course-index div#wiki_panel div.profile-wrapper section.user-info input[type="button"], div#wiki_panel div.profile-wrapper section.user-info section.course-index input[type="button"], div.book-wrapper section.book-sidebar div#wiki_panel div.profile-wrapper section.user-info input[type="button"], div#wiki_panel div.profile-wrapper section.user-info div.book-wrapper section.book-sidebar input[type="button"], div.info-wrapper section.handouts div#wiki_panel div.profile-wrapper section.user-info input[type="button"], div#wiki_panel div.profile-wrapper section.user-info div.info-wrapper section.handouts input[type="button"], div#wiki_panel div.profile-wrapper section.user-info input[type="button"], div#wiki_panel div.profile-wrapper section.user-info input[type="button"], div.discussion-wrapper aside div#wiki_panel div.profile-wrapper section.user-info input[type="button"], div#wiki_panel div.profile-wrapper section.user-info div.discussion-wrapper aside input[type="button"], .sidebar div#wiki_panel input[type="button"], div#wiki_panel .sidebar input[type="button"], section.course-index div#wiki_panel input[type="button"], div#wiki_panel section.course-index input[type="button"], div.book-wrapper section.book-sidebar div#wiki_panel input[type="button"], div#wiki_panel div.book-wrapper section.book-sidebar input[type="button"], div.info-wrapper section.handouts div#wiki_panel input[type="button"], div#wiki_panel div.info-wrapper section.handouts input[type="button"], div.profile-wrapper section.user-info div#wiki_panel input[type="button"], div#wiki_panel div.profile-wrapper section.user-info input[type="button"], div#wiki_panel input[type="button"], div.discussion-wrapper aside div#wiki_panel input[type="button"], div#wiki_panel div.discussion-wrapper aside input[type="button"], .sidebar div.discussion-wrapper aside div#wiki_panel input[type="button"], div.discussion-wrapper aside div#wiki_panel .sidebar input[type="button"], section.course-index div.discussion-wrapper aside div#wiki_panel input[type="button"], div.discussion-wrapper aside div#wiki_panel section.course-index input[type="button"], div.book-wrapper section.book-sidebar div.discussion-wrapper aside div#wiki_panel input[type="button"], div.discussion-wrapper aside div#wiki_panel div.book-wrapper section.book-sidebar input[type="button"], div.info-wrapper section.handouts div.discussion-wrapper aside div#wiki_panel input[type="button"], div.discussion-wrapper aside div#wiki_panel div.info-wrapper section.handouts input[type="button"], div.profile-wrapper section.user-info div.discussion-wrapper aside div#wiki_panel input[type="button"], div.discussion-wrapper aside div#wiki_panel div.profile-wrapper section.user-info input[type="button"], div.discussion-wrapper aside div#wiki_panel input[type="button"], div.discussion-wrapper aside div#wiki_panel input[type="button"], .sidebar div#wiki_panel div.discussion-wrapper aside input[type="button"], div#wiki_panel div.discussion-wrapper aside .sidebar input[type="button"], section.course-index div#wiki_panel div.discussion-wrapper aside input[type="button"], div#wiki_panel div.discussion-wrapper aside section.course-index input[type="button"], div.book-wrapper section.book-sidebar div#wiki_panel div.discussion-wrapper aside input[type="button"], div#wiki_panel div.discussion-wrapper aside div.book-wrapper section.book-sidebar input[type="button"], div.info-wrapper section.handouts div#wiki_panel div.discussion-wrapper aside input[type="button"], div#wiki_panel div.discussion-wrapper aside div.info-wrapper section.handouts input[type="button"], div.profile-wrapper section.user-info div#wiki_panel div.discussion-wrapper aside input[type="button"], div#wiki_panel div.discussion-wrapper aside div.profile-wrapper section.user-info input[type="button"], div#wiki_panel div.discussion-wrapper aside input[type="button"], div#wiki_panel div.discussion-wrapper aside input[type="button"], .sidebar div.info-wrapper section.handouts h1, div.info-wrapper section.handouts .sidebar h1, section.course-index div.info-wrapper section.handouts h1, div.info-wrapper section.handouts section.course-index h1, div.book-wrapper section.book-sidebar div.info-wrapper section.handouts h1, div.info-wrapper section.handouts div.book-wrapper section.book-sidebar h1, div.info-wrapper section.handouts h1, div.profile-wrapper section.user-info div.info-wrapper section.handouts h1, div.info-wrapper section.handouts div.profile-wrapper section.user-info h1, div#wiki_panel div.info-wrapper section.handouts h1, div.info-wrapper section.handouts div#wiki_panel h1, div.discussion-wrapper aside div.info-wrapper section.handouts h1, div.info-wrapper section.handouts div.discussion-wrapper aside h1, .sidebar div.profile-wrapper section.user-info header, div.profile-wrapper section.user-info .sidebar header, section.course-index div.profile-wrapper section.user-info header, div.profile-wrapper section.user-info section.course-index header, div.book-wrapper section.book-sidebar div.profile-wrapper section.user-info header, div.profile-wrapper section.user-info div.book-wrapper section.book-sidebar header, div.info-wrapper section.handouts div.profile-wrapper section.user-info header, div.profile-wrapper section.user-info div.info-wrapper section.handouts header, div.profile-wrapper section.user-info header, div#wiki_panel div.profile-wrapper section.user-info header, div.profile-wrapper section.user-info div#wiki_panel header, div.discussion-wrapper aside div.profile-wrapper section.user-info header, div.profile-wrapper section.user-info div.discussion-wrapper aside header, .sidebar div#wiki_panel h2, div#wiki_panel .sidebar h2, section.course-index div#wiki_panel h2, div#wiki_panel section.course-index h2, div.book-wrapper section.book-sidebar div#wiki_panel h2, div#wiki_panel div.book-wrapper section.book-sidebar h2, div.info-wrapper section.handouts div#wiki_panel h2, div#wiki_panel div.info-wrapper section.handouts h2, div.profile-wrapper section.user-info div#wiki_panel h2, div#wiki_panel div.profile-wrapper section.user-info h2, div#wiki_panel h2, div.discussion-wrapper aside div#wiki_panel h2, div#wiki_panel div.discussion-wrapper aside h2, .sidebar div.discussion-wrapper aside h1, div.discussion-wrapper aside .sidebar h1, section.course-index div.discussion-wrapper aside h1, div.discussion-wrapper aside section.course-index h1, div.book-wrapper section.book-sidebar div.discussion-wrapper aside h1, div.discussion-wrapper aside div.book-wrapper section.book-sidebar h1, div.info-wrapper section.handouts div.discussion-wrapper aside h1, div.discussion-wrapper aside div.info-wrapper section.handouts h1, div.profile-wrapper section.user-info div.discussion-wrapper aside h1, div.discussion-wrapper aside div.profile-wrapper section.user-info h1, div#wiki_panel div.discussion-wrapper aside h1, div.discussion-wrapper aside div#wiki_panel h1, div.discussion-wrapper aside h1 { +.sidebar .bottom-border, section.course-index .bottom-border, div.book-wrapper section.book-sidebar .bottom-border, div.info-wrapper section.handouts .bottom-border, div.profile-wrapper section.user-info .bottom-border, div#wiki_panel .bottom-border, div.discussion-wrapper aside .bottom-border, .sidebar h3, section.course-index .sidebar h3, .sidebar section.course-index h3, div.book-wrapper section.book-sidebar .sidebar h3, .sidebar div.book-wrapper section.book-sidebar h3, div.info-wrapper section.handouts .sidebar h3, .sidebar div.info-wrapper section.handouts h3, div.profile-wrapper section.user-info .sidebar h3, .sidebar div.profile-wrapper section.user-info h3, div#wiki_panel .sidebar h3, .sidebar div#wiki_panel h3, div.discussion-wrapper aside .sidebar h3, .sidebar div.discussion-wrapper aside h3, .sidebar section.course-index h3, section.course-index .sidebar h3, section.course-index h3, div.book-wrapper section.book-sidebar section.course-index h3, section.course-index div.book-wrapper section.book-sidebar h3, div.info-wrapper section.handouts section.course-index h3, section.course-index div.info-wrapper section.handouts h3, div.profile-wrapper section.user-info section.course-index h3, section.course-index div.profile-wrapper section.user-info h3, div#wiki_panel section.course-index h3, section.course-index div#wiki_panel h3, div.discussion-wrapper aside section.course-index h3, section.course-index div.discussion-wrapper aside h3, .sidebar div.book-wrapper section.book-sidebar h3, div.book-wrapper section.book-sidebar .sidebar h3, section.course-index div.book-wrapper section.book-sidebar h3, div.book-wrapper section.book-sidebar section.course-index h3, div.book-wrapper section.book-sidebar h3, div.info-wrapper section.handouts div.book-wrapper section.book-sidebar h3, div.book-wrapper section.book-sidebar div.info-wrapper section.handouts h3, div.profile-wrapper section.user-info div.book-wrapper section.book-sidebar h3, div.book-wrapper section.book-sidebar div.profile-wrapper section.user-info h3, div#wiki_panel div.book-wrapper section.book-sidebar h3, div.book-wrapper section.book-sidebar div#wiki_panel h3, div.discussion-wrapper aside div.book-wrapper section.book-sidebar h3, div.book-wrapper section.book-sidebar div.discussion-wrapper aside h3, .sidebar div.info-wrapper section.handouts h3, div.info-wrapper section.handouts .sidebar h3, section.course-index div.info-wrapper section.handouts h3, div.info-wrapper section.handouts section.course-index h3, div.book-wrapper section.book-sidebar div.info-wrapper section.handouts h3, div.info-wrapper section.handouts div.book-wrapper section.book-sidebar h3, div.info-wrapper section.handouts h3, div.profile-wrapper section.user-info div.info-wrapper section.handouts h3, div.info-wrapper section.handouts div.profile-wrapper section.user-info h3, div#wiki_panel div.info-wrapper section.handouts h3, div.info-wrapper section.handouts div#wiki_panel h3, div.discussion-wrapper aside div.info-wrapper section.handouts h3, div.info-wrapper section.handouts div.discussion-wrapper aside h3, .sidebar div.profile-wrapper section.user-info h3, div.profile-wrapper section.user-info .sidebar h3, section.course-index div.profile-wrapper section.user-info h3, div.profile-wrapper section.user-info section.course-index h3, div.book-wrapper section.book-sidebar div.profile-wrapper section.user-info h3, div.profile-wrapper section.user-info div.book-wrapper section.book-sidebar h3, div.info-wrapper section.handouts div.profile-wrapper section.user-info h3, div.profile-wrapper section.user-info div.info-wrapper section.handouts h3, div.profile-wrapper section.user-info h3, div#wiki_panel div.profile-wrapper section.user-info h3, div.profile-wrapper section.user-info div#wiki_panel h3, div.discussion-wrapper aside div.profile-wrapper section.user-info h3, div.profile-wrapper section.user-info div.discussion-wrapper aside h3, .sidebar div#wiki_panel h3, div#wiki_panel .sidebar h3, section.course-index div#wiki_panel h3, div#wiki_panel section.course-index h3, div.book-wrapper section.book-sidebar div#wiki_panel h3, div#wiki_panel div.book-wrapper section.book-sidebar h3, div.info-wrapper section.handouts div#wiki_panel h3, div#wiki_panel div.info-wrapper section.handouts h3, div.profile-wrapper section.user-info div#wiki_panel h3, div#wiki_panel div.profile-wrapper section.user-info h3, div#wiki_panel h3, div.discussion-wrapper aside div#wiki_panel h3, div#wiki_panel div.discussion-wrapper aside h3, .sidebar div.discussion-wrapper aside h3, div.discussion-wrapper aside .sidebar h3, section.course-index div.discussion-wrapper aside h3, div.discussion-wrapper aside section.course-index h3, div.book-wrapper section.book-sidebar div.discussion-wrapper aside h3, div.discussion-wrapper aside div.book-wrapper section.book-sidebar h3, div.info-wrapper section.handouts div.discussion-wrapper aside h3, div.discussion-wrapper aside div.info-wrapper section.handouts h3, div.profile-wrapper section.user-info div.discussion-wrapper aside h3, div.discussion-wrapper aside div.profile-wrapper section.user-info h3, div#wiki_panel div.discussion-wrapper aside h3, div.discussion-wrapper aside div#wiki_panel h3, div.discussion-wrapper aside h3, .sidebar div#wiki_panel input[type="button"], section.course-index .sidebar div#wiki_panel input[type="button"], .sidebar div#wiki_panel section.course-index input[type="button"], div.book-wrapper section.book-sidebar .sidebar div#wiki_panel input[type="button"], .sidebar div#wiki_panel div.book-wrapper section.book-sidebar input[type="button"], div.info-wrapper section.handouts .sidebar div#wiki_panel input[type="button"], .sidebar div#wiki_panel div.info-wrapper section.handouts input[type="button"], div.profile-wrapper section.user-info .sidebar div#wiki_panel input[type="button"], .sidebar div#wiki_panel div.profile-wrapper section.user-info input[type="button"], .sidebar div#wiki_panel input[type="button"], div.discussion-wrapper aside .sidebar div#wiki_panel input[type="button"], .sidebar div#wiki_panel div.discussion-wrapper aside input[type="button"], div#wiki_panel .sidebar input[type="button"], section.course-index div#wiki_panel .sidebar input[type="button"], div#wiki_panel .sidebar section.course-index input[type="button"], div.book-wrapper section.book-sidebar div#wiki_panel .sidebar input[type="button"], div#wiki_panel .sidebar div.book-wrapper section.book-sidebar input[type="button"], div.info-wrapper section.handouts div#wiki_panel .sidebar input[type="button"], div#wiki_panel .sidebar div.info-wrapper section.handouts input[type="button"], div.profile-wrapper section.user-info div#wiki_panel .sidebar input[type="button"], div#wiki_panel .sidebar div.profile-wrapper section.user-info input[type="button"], div#wiki_panel .sidebar input[type="button"], div.discussion-wrapper aside div#wiki_panel .sidebar input[type="button"], div#wiki_panel .sidebar div.discussion-wrapper aside input[type="button"], .sidebar section.course-index div#wiki_panel input[type="button"], section.course-index div#wiki_panel .sidebar input[type="button"], section.course-index div#wiki_panel input[type="button"], div.book-wrapper section.book-sidebar section.course-index div#wiki_panel input[type="button"], section.course-index div#wiki_panel div.book-wrapper section.book-sidebar input[type="button"], div.info-wrapper section.handouts section.course-index div#wiki_panel input[type="button"], section.course-index div#wiki_panel div.info-wrapper section.handouts input[type="button"], div.profile-wrapper section.user-info section.course-index div#wiki_panel input[type="button"], section.course-index div#wiki_panel div.profile-wrapper section.user-info input[type="button"], section.course-index div#wiki_panel input[type="button"], div.discussion-wrapper aside section.course-index div#wiki_panel input[type="button"], section.course-index div#wiki_panel div.discussion-wrapper aside input[type="button"], .sidebar div#wiki_panel section.course-index input[type="button"], div#wiki_panel section.course-index .sidebar input[type="button"], div#wiki_panel section.course-index input[type="button"], div.book-wrapper section.book-sidebar div#wiki_panel section.course-index input[type="button"], div#wiki_panel section.course-index div.book-wrapper section.book-sidebar input[type="button"], div.info-wrapper section.handouts div#wiki_panel section.course-index input[type="button"], div#wiki_panel section.course-index div.info-wrapper section.handouts input[type="button"], div.profile-wrapper section.user-info div#wiki_panel section.course-index input[type="button"], div#wiki_panel section.course-index div.profile-wrapper section.user-info input[type="button"], div#wiki_panel section.course-index input[type="button"], div.discussion-wrapper aside div#wiki_panel section.course-index input[type="button"], div#wiki_panel section.course-index div.discussion-wrapper aside input[type="button"], .sidebar div.book-wrapper section.book-sidebar div#wiki_panel input[type="button"], div.book-wrapper section.book-sidebar div#wiki_panel .sidebar input[type="button"], section.course-index div.book-wrapper section.book-sidebar div#wiki_panel input[type="button"], div.book-wrapper section.book-sidebar div#wiki_panel section.course-index input[type="button"], div.book-wrapper section.book-sidebar div#wiki_panel input[type="button"], div.info-wrapper section.handouts div.book-wrapper section.book-sidebar div#wiki_panel input[type="button"], div.book-wrapper section.book-sidebar div#wiki_panel div.info-wrapper section.handouts input[type="button"], div.profile-wrapper section.user-info div.book-wrapper section.book-sidebar div#wiki_panel input[type="button"], div.book-wrapper section.book-sidebar div#wiki_panel div.profile-wrapper section.user-info input[type="button"], div.book-wrapper section.book-sidebar div#wiki_panel input[type="button"], div.discussion-wrapper aside div.book-wrapper section.book-sidebar div#wiki_panel input[type="button"], div.book-wrapper section.book-sidebar div#wiki_panel div.discussion-wrapper aside input[type="button"], .sidebar div#wiki_panel div.book-wrapper section.book-sidebar input[type="button"], div#wiki_panel div.book-wrapper section.book-sidebar .sidebar input[type="button"], section.course-index div#wiki_panel div.book-wrapper section.book-sidebar input[type="button"], div#wiki_panel div.book-wrapper section.book-sidebar section.course-index input[type="button"], div#wiki_panel div.book-wrapper section.book-sidebar input[type="button"], div.info-wrapper section.handouts div#wiki_panel div.book-wrapper section.book-sidebar input[type="button"], div#wiki_panel div.book-wrapper section.book-sidebar div.info-wrapper section.handouts input[type="button"], div.profile-wrapper section.user-info div#wiki_panel div.book-wrapper section.book-sidebar input[type="button"], div#wiki_panel div.book-wrapper section.book-sidebar div.profile-wrapper section.user-info input[type="button"], div#wiki_panel div.book-wrapper section.book-sidebar input[type="button"], div.discussion-wrapper aside div#wiki_panel div.book-wrapper section.book-sidebar input[type="button"], div#wiki_panel div.book-wrapper section.book-sidebar div.discussion-wrapper aside input[type="button"], .sidebar div.info-wrapper section.handouts div#wiki_panel input[type="button"], div.info-wrapper section.handouts div#wiki_panel .sidebar input[type="button"], section.course-index div.info-wrapper section.handouts div#wiki_panel input[type="button"], div.info-wrapper section.handouts div#wiki_panel section.course-index input[type="button"], div.book-wrapper section.book-sidebar div.info-wrapper section.handouts div#wiki_panel input[type="button"], div.info-wrapper section.handouts div#wiki_panel div.book-wrapper section.book-sidebar input[type="button"], div.info-wrapper section.handouts div#wiki_panel input[type="button"], div.profile-wrapper section.user-info div.info-wrapper section.handouts div#wiki_panel input[type="button"], div.info-wrapper section.handouts div#wiki_panel div.profile-wrapper section.user-info input[type="button"], div.info-wrapper section.handouts div#wiki_panel input[type="button"], div.discussion-wrapper aside div.info-wrapper section.handouts div#wiki_panel input[type="button"], div.info-wrapper section.handouts div#wiki_panel div.discussion-wrapper aside input[type="button"], .sidebar div#wiki_panel div.info-wrapper section.handouts input[type="button"], div#wiki_panel div.info-wrapper section.handouts .sidebar input[type="button"], section.course-index div#wiki_panel div.info-wrapper section.handouts input[type="button"], div#wiki_panel div.info-wrapper section.handouts section.course-index input[type="button"], div.book-wrapper section.book-sidebar div#wiki_panel div.info-wrapper section.handouts input[type="button"], div#wiki_panel div.info-wrapper section.handouts div.book-wrapper section.book-sidebar input[type="button"], div#wiki_panel div.info-wrapper section.handouts input[type="button"], div.profile-wrapper section.user-info div#wiki_panel div.info-wrapper section.handouts input[type="button"], div#wiki_panel div.info-wrapper section.handouts div.profile-wrapper section.user-info input[type="button"], div#wiki_panel div.info-wrapper section.handouts input[type="button"], div.discussion-wrapper aside div#wiki_panel div.info-wrapper section.handouts input[type="button"], div#wiki_panel div.info-wrapper section.handouts div.discussion-wrapper aside input[type="button"], .sidebar div.profile-wrapper section.user-info div#wiki_panel input[type="button"], div.profile-wrapper section.user-info div#wiki_panel .sidebar input[type="button"], section.course-index div.profile-wrapper section.user-info div#wiki_panel input[type="button"], div.profile-wrapper section.user-info div#wiki_panel section.course-index input[type="button"], div.book-wrapper section.book-sidebar div.profile-wrapper section.user-info div#wiki_panel input[type="button"], div.profile-wrapper section.user-info div#wiki_panel div.book-wrapper section.book-sidebar input[type="button"], div.info-wrapper section.handouts div.profile-wrapper section.user-info div#wiki_panel input[type="button"], div.profile-wrapper section.user-info div#wiki_panel div.info-wrapper section.handouts input[type="button"], div.profile-wrapper section.user-info div#wiki_panel input[type="button"], div.profile-wrapper section.user-info div#wiki_panel input[type="button"], div.discussion-wrapper aside div.profile-wrapper section.user-info div#wiki_panel input[type="button"], div.profile-wrapper section.user-info div#wiki_panel div.discussion-wrapper aside input[type="button"], .sidebar div#wiki_panel div.profile-wrapper section.user-info input[type="button"], div#wiki_panel div.profile-wrapper section.user-info .sidebar input[type="button"], section.course-index div#wiki_panel div.profile-wrapper section.user-info input[type="button"], div#wiki_panel div.profile-wrapper section.user-info section.course-index input[type="button"], div.book-wrapper section.book-sidebar div#wiki_panel div.profile-wrapper section.user-info input[type="button"], div#wiki_panel div.profile-wrapper section.user-info div.book-wrapper section.book-sidebar input[type="button"], div.info-wrapper section.handouts div#wiki_panel div.profile-wrapper section.user-info input[type="button"], div#wiki_panel div.profile-wrapper section.user-info div.info-wrapper section.handouts input[type="button"], div#wiki_panel div.profile-wrapper section.user-info input[type="button"], div#wiki_panel div.profile-wrapper section.user-info input[type="button"], div.discussion-wrapper aside div#wiki_panel div.profile-wrapper section.user-info input[type="button"], div#wiki_panel div.profile-wrapper section.user-info div.discussion-wrapper aside input[type="button"], .sidebar div#wiki_panel input[type="button"], div#wiki_panel .sidebar input[type="button"], section.course-index div#wiki_panel input[type="button"], div#wiki_panel section.course-index input[type="button"], div.book-wrapper section.book-sidebar div#wiki_panel input[type="button"], div#wiki_panel div.book-wrapper section.book-sidebar input[type="button"], div.info-wrapper section.handouts div#wiki_panel input[type="button"], div#wiki_panel div.info-wrapper section.handouts input[type="button"], div.profile-wrapper section.user-info div#wiki_panel input[type="button"], div#wiki_panel div.profile-wrapper section.user-info input[type="button"], div#wiki_panel input[type="button"], div.discussion-wrapper aside div#wiki_panel input[type="button"], div#wiki_panel div.discussion-wrapper aside input[type="button"], .sidebar div.discussion-wrapper aside div#wiki_panel input[type="button"], div.discussion-wrapper aside div#wiki_panel .sidebar input[type="button"], section.course-index div.discussion-wrapper aside div#wiki_panel input[type="button"], div.discussion-wrapper aside div#wiki_panel section.course-index input[type="button"], div.book-wrapper section.book-sidebar div.discussion-wrapper aside div#wiki_panel input[type="button"], div.discussion-wrapper aside div#wiki_panel div.book-wrapper section.book-sidebar input[type="button"], div.info-wrapper section.handouts div.discussion-wrapper aside div#wiki_panel input[type="button"], div.discussion-wrapper aside div#wiki_panel div.info-wrapper section.handouts input[type="button"], div.profile-wrapper section.user-info div.discussion-wrapper aside div#wiki_panel input[type="button"], div.discussion-wrapper aside div#wiki_panel div.profile-wrapper section.user-info input[type="button"], div.discussion-wrapper aside div#wiki_panel input[type="button"], div.discussion-wrapper aside div#wiki_panel input[type="button"], .sidebar div#wiki_panel div.discussion-wrapper aside input[type="button"], div#wiki_panel div.discussion-wrapper aside .sidebar input[type="button"], section.course-index div#wiki_panel div.discussion-wrapper aside input[type="button"], div#wiki_panel div.discussion-wrapper aside section.course-index input[type="button"], div.book-wrapper section.book-sidebar div#wiki_panel div.discussion-wrapper aside input[type="button"], div#wiki_panel div.discussion-wrapper aside div.book-wrapper section.book-sidebar input[type="button"], div.info-wrapper section.handouts div#wiki_panel div.discussion-wrapper aside input[type="button"], div#wiki_panel div.discussion-wrapper aside div.info-wrapper section.handouts input[type="button"], div.profile-wrapper section.user-info div#wiki_panel div.discussion-wrapper aside input[type="button"], div#wiki_panel div.discussion-wrapper aside div.profile-wrapper section.user-info input[type="button"], div#wiki_panel div.discussion-wrapper aside input[type="button"], div#wiki_panel div.discussion-wrapper aside input[type="button"], .sidebar div.info-wrapper section.handouts header, div.info-wrapper section.handouts .sidebar header, section.course-index div.info-wrapper section.handouts header, div.info-wrapper section.handouts section.course-index header, div.book-wrapper section.book-sidebar div.info-wrapper section.handouts header, div.info-wrapper section.handouts div.book-wrapper section.book-sidebar header, div.info-wrapper section.handouts header, div.profile-wrapper section.user-info div.info-wrapper section.handouts header, div.info-wrapper section.handouts div.profile-wrapper section.user-info header, div#wiki_panel div.info-wrapper section.handouts header, div.info-wrapper section.handouts div#wiki_panel header, div.discussion-wrapper aside div.info-wrapper section.handouts header, div.info-wrapper section.handouts div.discussion-wrapper aside header, .sidebar div.profile-wrapper section.user-info header, div.profile-wrapper section.user-info .sidebar header, section.course-index div.profile-wrapper section.user-info header, div.profile-wrapper section.user-info section.course-index header, div.book-wrapper section.book-sidebar div.profile-wrapper section.user-info header, div.profile-wrapper section.user-info div.book-wrapper section.book-sidebar header, div.info-wrapper section.handouts div.profile-wrapper section.user-info header, div.profile-wrapper section.user-info div.info-wrapper section.handouts header, div.profile-wrapper section.user-info header, div#wiki_panel div.profile-wrapper section.user-info header, div.profile-wrapper section.user-info div#wiki_panel header, div.discussion-wrapper aside div.profile-wrapper section.user-info header, div.profile-wrapper section.user-info div.discussion-wrapper aside header, .sidebar div#wiki_panel h2, div#wiki_panel .sidebar h2, section.course-index div#wiki_panel h2, div#wiki_panel section.course-index h2, div.book-wrapper section.book-sidebar div#wiki_panel h2, div#wiki_panel div.book-wrapper section.book-sidebar h2, div.info-wrapper section.handouts div#wiki_panel h2, div#wiki_panel div.info-wrapper section.handouts h2, div.profile-wrapper section.user-info div#wiki_panel h2, div#wiki_panel div.profile-wrapper section.user-info h2, div#wiki_panel h2, div.discussion-wrapper aside div#wiki_panel h2, div#wiki_panel div.discussion-wrapper aside h2, .sidebar div.discussion-wrapper aside h1, div.discussion-wrapper aside .sidebar h1, section.course-index div.discussion-wrapper aside h1, div.discussion-wrapper aside section.course-index h1, div.book-wrapper section.book-sidebar div.discussion-wrapper aside h1, div.discussion-wrapper aside div.book-wrapper section.book-sidebar h1, div.info-wrapper section.handouts div.discussion-wrapper aside h1, div.discussion-wrapper aside div.info-wrapper section.handouts h1, div.profile-wrapper section.user-info div.discussion-wrapper aside h1, div.discussion-wrapper aside div.profile-wrapper section.user-info h1, div#wiki_panel div.discussion-wrapper aside h1, div.discussion-wrapper aside div#wiki_panel h1, div.discussion-wrapper aside h1 { -webkit-box-shadow: 0 1px 0 #eeeeee; -moz-box-shadow: 0 1px 0 #eeeeee; box-shadow: 0 1px 0 #eeeeee; @@ -3739,31 +3739,56 @@ div.info-wrapper section.handouts { border-radius: 0 4px 4px 0; border-right: 0; border-left: 1px solid #d3d3d3; } -div.info-wrapper section.handouts h1 { - padding: 11.326px 22.652px; +div.info-wrapper section.handouts header { + padding: 11.326px 16.989px; } +div.info-wrapper section.handouts header h1 { font-size: 18px; margin: 0; } +div.info-wrapper section.handouts header p { + margin-bottom: 0; + margin-top: 4px; + font-size: 12px; + color: #666; } div.info-wrapper section.handouts ol { list-style: none; } div.info-wrapper section.handouts ol li { -webkit-box-shadow: 0 1px 0 #eeeeee; -moz-box-shadow: 0 1px 0 #eeeeee; box-shadow: 0 1px 0 #eeeeee; - border-bottom: 1px solid #d3d3d3; } + border-bottom: 1px solid #d3d3d3; + padding: 7px 16.989px; } +div.info-wrapper section.handouts ol li:hover { + background-color: #e9e9e9; } +div.info-wrapper section.handouts ol li h3, div.info-wrapper section.handouts ol li div#wiki_panel input[type="button"], div#wiki_panel div.info-wrapper section.handouts ol li input[type="button"] { + border-bottom: 0; + text-transform: uppercase; + font-weight: bold; + color: #999; + -webkit-box-shadow: none; + -moz-box-shadow: none; + box-shadow: none; + font-size: 12px; } div.info-wrapper section.handouts ol li p { - padding: 7px 22.652px; margin: 0; text-transform: none; letter-spacing: 0; font-size: 14px; } -div.info-wrapper section.handouts ol li p:hover { - background: #efefef; } div.info-wrapper section.handouts ol li p a { - display: inline; - padding: 0; } -div.info-wrapper section.handouts ol li p a:hover { - text-decoration: underline; - background: none; } + padding-right: 8px; } +div.info-wrapper section.handouts ol li p a:before { + content: "•"; + display: -moz-inline-box; + -moz-box-orient: vertical; + display: inline-block; + vertical-align: baseline; + zoom: 1; + *display: inline; + *vertical-align: auto; + padding-right: 8px; + color: #ccc; } +div.info-wrapper section.handouts ol li p a:first-child:before { + content: ""; + padding-right: 0; } div.info-wrapper section.handouts ol li a { -webkit-transition-property: all; -moz-transition-property: all; @@ -3786,11 +3811,16 @@ div.info-wrapper section.handouts ol li a { -o-transition-delay: 0; transition-delay: 0; color: #4d4d4d; - display: block; - padding: 7px 22.652px; - text-decoration: none; } + text-decoration: none; + display: -moz-inline-box; + -moz-box-orient: vertical; + display: inline-block; + vertical-align: baseline; + zoom: 1; + *display: inline; + *vertical-align: auto; } div.info-wrapper section.handouts ol li a:hover { - background: #efefef; } + color: #993333; } div.profile-wrapper { color: #000; } diff --git a/templates/sass/_info.scss b/templates/sass/_info.scss index 353aca75b2..fe0bbcaa6c 100644 --- a/templates/sass/_info.scss +++ b/templates/sass/_info.scss @@ -59,11 +59,21 @@ div.info-wrapper { border-right: 0; border-left: 1px solid #d3d3d3; - h1 { - padding: lh(.5) lh(); - font-size: 18px; - margin: 0 ; + header { @extend .bottom-border; + padding: lh(.5) lh(.75); + + h1 { + font-size: 18px; + margin: 0 ; + } + + p { + margin-bottom: 0; + margin-top: 4px; + font-size: 12px; + color: #666; + } } ol { @@ -73,25 +83,42 @@ div.info-wrapper { @include box-shadow(0 1px 0 #eee); border-bottom: 1px solid #d3d3d3; @extend .clearfix; + padding: 7px lh(.75); + + &:hover { + background-color: #e9e9e9; + } + + h3 { + border-bottom: 0; + text-transform: uppercase; + font-weight: bold; + color: #999; + @include box-shadow(none); + font-size: 12px; + } p { - padding: 7px lh(); margin: 0; text-transform: none; letter-spacing: 0; font-size: $body-font-size; - &:hover { - background: #efefef; - } - a { - display: inline; - padding: 0; + padding-right: 8px; - &:hover { - text-decoration: underline; - background: none; + &:before { + content: "•"; + @include inline-block(); + padding-right: 8px; + color: #ccc; + } + + &:first-child { + &:before { + content: ""; + padding-right: 0; + } } } } @@ -99,12 +126,11 @@ div.info-wrapper { a { @include transition(); color: lighten($text-color, 10%); - display: block; - padding: 7px lh(); text-decoration: none; + @include inline-block(); &:hover { - background: #efefef; + color: $mit-red; } } } From 0704bb378773d014fd6aadda3c95eade92709a5e Mon Sep 17 00:00:00 2001 From: Kyle Fiedler Date: Mon, 26 Mar 2012 11:18:05 -0400 Subject: [PATCH 43/50] Added lectures and solutions in epandable lists --- static/css/application.css | 40 +++++++++++++++++++++++++++++-- templates/info.html | 18 ++++++++++++++ templates/sass/_info.scss | 49 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 105 insertions(+), 2 deletions(-) diff --git a/static/css/application.css b/static/css/application.css index 0187781730..c630ba9128 100644 --- a/static/css/application.css +++ b/static/css/application.css @@ -3750,15 +3750,51 @@ div.info-wrapper section.handouts header p { font-size: 12px; color: #666; } div.info-wrapper section.handouts ol { - list-style: none; } + list-style: none; + background: none; } div.info-wrapper section.handouts ol li { -webkit-box-shadow: 0 1px 0 #eeeeee; -moz-box-shadow: 0 1px 0 #eeeeee; box-shadow: 0 1px 0 #eeeeee; border-bottom: 1px solid #d3d3d3; - padding: 7px 16.989px; } + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + padding: 7px 16.989px; + background: none; + position: relative; } +div.info-wrapper section.handouts ol li.expandable h4, div.info-wrapper section.handouts ol li.collapsable h4 { + padding-left: 18px; + font-style: 14px; + font-weight: normal; } +div.info-wrapper section.handouts ol li ul { + background: none; + margin: 7px -16.989px 0; } +div.info-wrapper section.handouts ol li ul li { + padding-left: 34.989px; + -webkit-box-shadow: inset 0 1px 0 #eeeeee; + -moz-box-shadow: inset 0 1px 0 #eeeeee; + box-shadow: inset 0 1px 0 #eeeeee; + border-top: 1px solid #d3d3d3; + border-bottom: 0; } div.info-wrapper section.handouts ol li:hover { background-color: #e9e9e9; } +div.info-wrapper section.handouts ol li div.hitarea { + background-image: url("/static/images/treeview-default.gif"); + width: 100%; + height: 100%; + max-height: 20px; + display: block; + position: absolute; + left: 16.989px; + margin-left: 0; } +div.info-wrapper section.handouts ol li div.hitarea:hover { + opacity: 0.6; + filter: alpha(opacity=60); } +div.info-wrapper section.handouts ol li div.hitarea.expandable-hitarea { + background-position: -80px 1px; } +div.info-wrapper section.handouts ol li div.hitarea.collapsable-hitarea { + background-position: -64px -21px; } div.info-wrapper section.handouts ol li h3, div.info-wrapper section.handouts ol li div#wiki_panel input[type="button"], div#wiki_panel div.info-wrapper section.handouts ol li input[type="button"] { border-bottom: 0; text-transform: uppercase; diff --git a/templates/info.html b/templates/info.html index 9489f5f1b1..3cc93e72d7 100644 --- a/templates/info.html +++ b/templates/info.html @@ -1,4 +1,22 @@ <%inherit file="main.html" /> + +<%block name="js_extra"> + + + <%block name="title">Course Info - MITx 6.002x <%include file="navigation.html" args="active_page='info'" /> diff --git a/templates/sass/_info.scss b/templates/sass/_info.scss index fe0bbcaa6c..d2d590e0f0 100644 --- a/templates/sass/_info.scss +++ b/templates/sass/_info.scss @@ -78,17 +78,66 @@ div.info-wrapper { ol { list-style: none; + background: none; li { @include box-shadow(0 1px 0 #eee); border-bottom: 1px solid #d3d3d3; + @include box-sizing(border-box); @extend .clearfix; padding: 7px lh(.75); + background: none; + position: relative; + + &.expandable, + &.collapsable { + h4 { + padding-left: 18px; + font-style: $body-font-size; + font-weight: normal; + } + } + + ul { + background: none; + margin: 7px (-(lh(.75))) 0; + + li { + padding-left: 18px + lh(.75); + @include box-shadow(inset 0 1px 0 #eee); + border-top: 1px solid #d3d3d3; + border-bottom: 0; + } + } &:hover { background-color: #e9e9e9; } + div.hitarea { + background-image: url('/static/images/treeview-default.gif'); + width: 100%; + height: 100%; + max-height: 20px; + display: block; + position: absolute; + left: lh(.75); + margin-left: 0; + + &:hover { + opacity: 0.6; + filter: alpha(opacity=60); + } + + &.expandable-hitarea { + background-position: -80px 1px; + } + + &.collapsable-hitarea { + background-position: -64px -21px; + } + } + h3 { border-bottom: 0; text-transform: uppercase; From abee61762283ee141def5e9af1293d094d680b57 Mon Sep 17 00:00:00 2001 From: Lyla Fischer Date: Thu, 5 Apr 2012 14:18:22 -0400 Subject: [PATCH 44/50] check button and "we already started" announcement Changed the format of the check/submit button so that the exam submission makes more sense; added a disclaimer to the enrollment number so that new students know that they will be disadvantaged. --- djangoapps/courseware/modules/capa_module.py | 11 +++++------ templates/create_account.html | 7 +++++-- templates/problem.html | 9 +++++++-- 3 files changed, 17 insertions(+), 10 deletions(-) diff --git a/djangoapps/courseware/modules/capa_module.py b/djangoapps/courseware/modules/capa_module.py index 7ec92ecd44..71f76fa66a 100644 --- a/djangoapps/courseware/modules/capa_module.py +++ b/djangoapps/courseware/modules/capa_module.py @@ -73,7 +73,9 @@ class Module(XModule): content={'name':self.name, 'html':html} - check_button = True + # We using strings as truthy values, because the terminology of the check button + # is context-specific. + check_button = "Grade" if self.max_attempts else "Check" reset_button = True save_button = True @@ -96,10 +98,6 @@ class Module(XModule): if not self.lcp.done: reset_button = False - attempts_str = "" - if self.max_attempts != None: - attempts_str = " ({a}/{m})".format(a=self.attempts, m=self.max_attempts) - # We don't need a "save" button if infinite number of attempts and non-randomized if self.max_attempts == None and self.rerandomize != "always": save_button = False @@ -122,7 +120,8 @@ class Module(XModule): 'save_button' : save_button, 'answer_available' : self.answer_available(), 'ajax_url' : self.ajax_url, - 'attempts': attempts_str, + 'attempts_used': self.attempts, + 'attempts_allowed': self.max_attempts, 'explain': explain }) if encapsulate: diff --git a/templates/create_account.html b/templates/create_account.html index 40cfaacd1c..3eee7a6bc1 100644 --- a/templates/create_account.html +++ b/templates/create_account.html @@ -5,7 +5,10 @@ - +

      + Please note that 6.002x has already started. + Several assignment due dates for 6.002x have already passed. It is now impossible for newly enrolled students to get 100% of the points in the course, although new students can still earn points for assignments whose due dates have not passed, and students have access to all of the course material that has been released for the course. +

      <% if 'error' in locals(): e = error %> @@ -69,5 +72,5 @@ -
      +
      diff --git a/templates/problem.html b/templates/problem.html index 965f5a9c38..f332dda378 100644 --- a/templates/problem.html +++ b/templates/problem.html @@ -7,10 +7,10 @@ % if check_button: - + % endif % if reset_button: - + % endif % if save_button: @@ -21,5 +21,10 @@ % if explain :
      Explanation % endif + % if attempts_allowed : +
      + You have used ${ attempts_used } of ${ attempts_allowed } submissions +
      + % endif From 949745daa6698c38563828fa1a6abbda271c356c Mon Sep 17 00:00:00 2001 From: David Ormsbee Date: Fri, 6 Apr 2012 15:49:30 -0400 Subject: [PATCH 45/50] Remove broken symlink to simplewiki. The only thing we used from that folder was the auto-suggest JavaScript, and that got moved over to mitx/static/js/simplewiki --- static/simplewiki | 1 - 1 file changed, 1 deletion(-) delete mode 120000 static/simplewiki diff --git a/static/simplewiki b/static/simplewiki deleted file mode 120000 index 1f643ef532..0000000000 --- a/static/simplewiki +++ /dev/null @@ -1 +0,0 @@ -../../mitx/simplewiki/media/ \ No newline at end of file From 14cd055e5e7fb26eafd60090a7dc63adaab4f3dd Mon Sep 17 00:00:00 2001 From: Bridger Maxwell Date: Fri, 6 Apr 2012 19:00:38 -0400 Subject: [PATCH 46/50] Two potential sources of error fixed for MathJax wiki bug. --- templates/main.html | 6 +++--- templates/simplewiki_base.html | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/templates/main.html b/templates/main.html index 1d30ee6167..f82e65c335 100644 --- a/templates/main.html +++ b/templates/main.html @@ -20,10 +20,10 @@ displayMath: [["\\[","\\]"]]} }); + <%block name="headextra"/> + + - -<%block name="headextra"/> - "> diff --git a/templates/simplewiki_base.html b/templates/simplewiki_base.html index 0f08d612c8..c31499c881 100644 --- a/templates/simplewiki_base.html +++ b/templates/simplewiki_base.html @@ -40,7 +40,7 @@ tex2jax: {inlineMath: [ ['$','$'], ["\\(","\\)"]], displayMath: [ ['$$','$$'], ["\\[","\\]"]]} }); - + From 1a1181c1af51df6bba33e53c9033e94b403585c4 Mon Sep 17 00:00:00 2001 From: Kyle Fiedler Date: Mon, 9 Apr 2012 11:52:03 -0400 Subject: [PATCH 48/50] Added generated css --- static/css/application.css | 46 +++++++++++--------------------------- 1 file changed, 13 insertions(+), 33 deletions(-) diff --git a/static/css/application.css b/static/css/application.css index 591cf6c94e..53116108e2 100644 --- a/static/css/application.css +++ b/static/css/application.css @@ -3097,9 +3097,9 @@ section.course-content div.video-subtitles div.video-wrapper section.video-contr -moz-box-shadow: 1px 0 0 #555555, inset 1px 0 0 #555555; box-shadow: 1px 0 0 #555555, inset 1px 0 0 #555555; float: left; - line-height: 46px; + line-height: 0; + padding-right: 5.663px; margin-right: 0; - position: relative; -webkit-transition-property: all; -moz-transition-property: all; -ms-transition-property: all; @@ -3120,23 +3120,17 @@ section.course-content div.video-subtitles div.video-wrapper section.video-contr -ms-transition-delay: 0; -o-transition-delay: 0; transition-delay: 0; - width: 110px; cursor: pointer; -webkit-font-smoothing: antialiased; } section.course-content div.video-subtitles div.video-wrapper section.video-controls div.secondary-controls div.speeds h3, section.course-content div.video-subtitles div.video-wrapper section.video-controls div.secondary-controls div.speeds div#wiki_panel input[type="button"], div#wiki_panel section.course-content div.video-subtitles div.video-wrapper section.video-controls div.secondary-controls div.speeds input[type="button"] { - display: -moz-inline-box; - -moz-box-orient: vertical; - display: inline-block; - vertical-align: baseline; - zoom: 1; - *display: inline; - *vertical-align: auto; + float: left; padding: 0 5.663px 0 11.326px; font-weight: normal; text-transform: uppercase; font-size: 12px; letter-spacing: 1px; - color: #999; } + color: #999; + line-height: 46px; } section.course-content div.video-subtitles div.video-wrapper section.video-controls div.secondary-controls div.speeds p.active { display: -moz-inline-box; -moz-box-orient: vertical; @@ -3147,34 +3141,22 @@ section.course-content div.video-subtitles div.video-wrapper section.video-contr *vertical-align: auto; padding: 0 11.326px 0 0; margin-bottom: 0; - font-weight: bold; } + font-weight: bold; + display: none; } section.course-content div.video-subtitles div.video-wrapper section.video-controls div.secondary-controls div.speeds ol#video_speeds { - background-color: #444; - border: 1px solid #000; - border-top: 0; - -webkit-box-shadow: inset 1px 0 0 #555555; - -moz-box-shadow: inset 1px 0 0 #555555; - box-shadow: inset 1px 0 0 #555555; display: -moz-inline-box; -moz-box-orient: vertical; display: inline-block; vertical-align: baseline; zoom: 1; *display: inline; - *vertical-align: auto; - left: -1px; - position: absolute; - top: 48px; - width: 100%; - z-index: 10; } + *vertical-align: auto; } section.course-content div.video-subtitles div.video-wrapper section.video-controls div.secondary-controls div.speeds ol#video_speeds li { - border-bottom: 1px solid #000; - -webkit-box-shadow: 0 1px 0 #555555; - -moz-box-shadow: 0 1px 0 #555555; - box-shadow: 0 1px 0 #555555; + float: left; color: #fff; cursor: pointer; - padding: 0 11.326px; } + padding: 0 5.663px; + line-height: 46px; } section.course-content div.video-subtitles div.video-wrapper section.video-controls div.secondary-controls div.speeds ol#video_speeds li.active { font-weight: bold; } section.course-content div.video-subtitles div.video-wrapper section.video-controls div.secondary-controls div.speeds ol#video_speeds li:last-child { @@ -3184,11 +3166,9 @@ section.course-content div.video-subtitles div.video-wrapper section.video-contr -moz-box-shadow: none; box-shadow: none; } section.course-content div.video-subtitles div.video-wrapper section.video-controls div.secondary-controls div.speeds ol#video_speeds li:hover { - color: #aaa; - background-color: #666; } -section.course-content div.video-subtitles div.video-wrapper section.video-controls div.secondary-controls div.speeds:hover { - opacity: 1; background-color: #444; } +section.course-content div.video-subtitles div.video-wrapper section.video-controls div.secondary-controls div.speeds:hover { + opacity: 1; } section.course-content div.video-subtitles div.video-wrapper section.video-controls div.secondary-controls a.hide-subtitles { float: left; display: block; From 6238f431b19a4a34069d265f79ade24fe8f4468b Mon Sep 17 00:00:00 2001 From: David Ormsbee Date: Mon, 9 Apr 2012 12:23:48 -0400 Subject: [PATCH 49/50] commenting out the use of S3 for static JS assets --- settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/settings.py b/settings.py index bd598b833e..2370611ca4 100644 --- a/settings.py +++ b/settings.py @@ -13,7 +13,7 @@ STATIC_GRAB = False DEV_CONTENT = True LIB_URL = '/static/js/' -LIB_URL = 'https://mitxstatic.s3.amazonaws.com/js/' +# LIB_URL = 'https://mitxstatic.s3.amazonaws.com/js/' # No longer using S3 for this BOOK_URL = '/static/book/' BOOK_URL = 'https://mitxstatic.s3.amazonaws.com/book_images/' From 6c44d00ab045932c051121568c998e192aba86db Mon Sep 17 00:00:00 2001 From: David Ormsbee Date: Mon, 9 Apr 2012 12:27:30 -0400 Subject: [PATCH 50/50] removed dead link to 3rdParty/static/js --- static/lib | 1 - 1 file changed, 1 deletion(-) delete mode 120000 static/lib diff --git a/static/lib b/static/lib deleted file mode 120000 index 834765babd..0000000000 --- a/static/lib +++ /dev/null @@ -1 +0,0 @@ -../../3rdParty/static/js/ \ No newline at end of file