You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

403 lines
47 KiB

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="utf-8">
  5. <meta http-equiv="X-UA-Compatible" content="IE=edge">
  6. <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
  7. <meta name="apple-mobile-web-app-capable" content="yes">
  8. <meta name="apple-mobile-web-app-status-bar-style" content="black">
  9. <meta name="mobile-web-app-capable" content="yes">
  10. <title>
  11. Lab 7: Scheduling tasks - HackMD
  12. </title>
  13. <link rel="icon" type="image/png" href="https://hackmd.io/favicon.png">
  14. <link rel="apple-touch-icon" href="https://hackmd.io/apple-touch-icon.png">
  15. <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha256-916EbMg70RQy9LHiGkXzG8hSg9EdNy97GazNG/aiY1w=" crossorigin="anonymous" />
  16. <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css" integrity="sha256-eZrrJcwDc/3uDhsdt61sL2oOBY362qM3lon1gyExkL0=" crossorigin="anonymous" />
  17. <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/ionicons/2.0.1/css/ionicons.min.css" integrity="sha256-3iu9jgsy9TpTwXKb7bNQzqWekRX7pPK+2OLj3R922fo=" crossorigin="anonymous" />
  18. <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/octicons/3.5.0/octicons.min.css" integrity="sha256-QiWfLIsCT02Sdwkogf6YMiQlj4NE84MKkzEMkZnMGdg=" crossorigin="anonymous" />
  19. <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.5.1/themes/prism.min.css" integrity="sha256-vtR0hSWRc3Tb26iuN2oZHt3KRUomwTufNIf5/4oeCyg=" crossorigin="anonymous" />
  20. <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@hackmd/emojify.js@2.1.0/dist/css/basic/emojify.min.css" integrity="sha256-UOrvMOsSDSrW6szVLe8ZDZezBxh5IoIfgTwdNDgTjiU=" crossorigin="anonymous" />
  21. <style>
  22. @import url(https://fonts.googleapis.com/css?family=Roboto:300,300i,400,400i,500,500i|Source+Code+Pro:300,400,500|Source+Sans+Pro:300,300i,400,400i,600,600i|Source+Serif+Pro&subset=latin-ext);.hljs{background:#fff;color:#333;display:block;overflow-x:auto;padding:.5em}.hljs-comment,.hljs-meta{color:#969896}.hljs-emphasis,.hljs-quote,.hljs-string,.hljs-strong,.hljs-template-variable,.hljs-variable{color:#df5000}.hljs-keyword,.hljs-selector-tag,.hljs-type{color:#a71d5d}.hljs-attribute,.hljs-bullet,.hljs-literal,.hljs-number,.hljs-symbol{color:#0086b3}.hljs-built_in,.hljs-builtin-name{color:#005cc5}.hljs-name,.hljs-section{color:#63a35c}.hljs-tag{color:#333}.hljs-attr,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-id,.hljs-selector-pseudo,.hljs-title{color:#795da3}.hljs-addition{background-color:#eaffea;color:#55a532}.hljs-deletion{background-color:#ffecec;color:#bd2c00}.hljs-link{text-decoration:underline}.markdown-body{word-wrap:break-word;font-size:16px;line-height:1.5}.markdown-body:after,.markdown-body:before{content:"";display:table}.markdown-body:after{clear:both}.markdown-body>:first-child{margin-top:0!important}.markdown-body>:last-child{margin-bottom:0!important}.markdown-body a:not([href]){color:inherit;text-decoration:none}.markdown-body .absent{color:#c00}.markdown-body .anchor{float:left;line-height:1;margin-left:-20px;padding-right:4px}.markdown-body .anchor:focus{outline:none}.markdown-body blockquote,.markdown-body dl,.markdown-body ol,.markdown-body p,.markdown-body pre,.markdown-body table,.markdown-body ul{margin-bottom:16px;margin-top:0}.markdown-body hr{background-color:#e7e7e7;border:0;height:.25em;margin:24px 0;padding:0}.markdown-body blockquote{border-left:.25em solid #ddd;color:#777;font-size:16px;padding:0 1em}.markdown-body blockquote>:first-child{margin-top:0}.markdown-body blockquote>:last-child{margin-bottom:0}.markdown-body kbd,.popover kbd{background-color:#fcfcfc;border:1px solid;border-color:#ccc #ccc #bbb;border-radius:3px;box-shadow:inset 0 -1px 0 #bbb;color:#555;display:inline-block;font-size:11px;line-height:10px;padding:3px 5px;vertical-align:middle}.markdown-body .loweralpha{list-style-type:lower-alpha}.markdown-body h1,.markdown-body h2,.markdown-body h3,.markdown-body h4,.markdown-body h5,.markdown-body h6{font-weight:600;line-height:1.25;margin-bottom:16px;margin-top:24px}.markdown-body h1 .octicon-link,.markdown-body h2 .octicon-link,.markdown-body h3 .octicon-link,.markdown-body h4 .octicon-link,.markdown-body h5 .octicon-link,.markdown-body h6 .octicon-link{color:#000;vertical-align:middle;visibility:hidden}.markdown-body h1:hover .anchor,.markdown-body h2:hover .anchor,.markdown-body h3:hover .anchor,.markdown-body h4:hover .anchor,.markdown-body h5:hover .anchor,.markdown-body h6:hover .anchor{text-decoration:none}.markdown-body h1:hover .anchor .octicon-link,.markdown-body h2:hover .anchor .octicon-link,.markdown-body h3:hover .anchor .octicon-link,.markdown-body h4:hover .anchor .octicon-link,.markdown-body h5:hover .anchor .octicon-link,.markdown-body h6:hover .anchor .octicon-link{visibility:visible}.markdown-body h1 code,.markdown-body h1 tt,.markdown-body h2 code,.markdown-body h2 tt,.markdown-body h3 code,.markdown-body h3 tt,.markdown-body h4 code,.markdown-body h4 tt,.markdown-body h5 code,.markdown-body h5 tt,.markdown-body h6 code,.markdown-body h6 tt{font-size:inherit}.markdown-body h1{font-size:2em}.markdown-body h1,.markdown-body h2{border-bottom:1px solid #eee;padding-bottom:.3em}.markdown-body h2{font-size:1.5em}.markdown-body h3{font-size:1.25em}.markdown-body h4{font-size:1em}.markdown-body h5{font-size:.875em}.markdown-body h6{color:#777;font-size:.85em}.markdown-body ol,.markdown-body ul{padding-left:2em}.markdown-body ol.no-list,.markdown-body ul.no-list{list-style-type:none;padding:0}.markdown-body ol ol,.markdown-body ol ul,.markdown-body ul ol,.markdown-body ul ul{margin-bottom:0;margin-top:0}.markdown-body li>p{margin-top:16px}.markdown-body li+li{padding-top:.25em}.markdown-body dl{padding:0}.markdown-body dl dt{font-size:1em;font-style:italic;font-weight:700;margin-top:16px;padding:0}.markdown-body dl dd{margin-bottom:16px;padding:0 16px}.markdown-body table{display:block;overflow:auto;width:100%;word-break:normal;word-break:keep-all}.markdown-body table th{font-weight:700}.markdown-body table td,.markdown-body table th{border:1px solid #ddd;padding:6px 13px}.markdown-body table tr{background-color:#fff;border-top:1px solid #ccc}.markdown-body table tr:nth-child(2n){background-color:#f8f8f8}.markdown-body img{background-color:#fff;box-sizing:initial;max-width:100%}.markdown-body img[align=right]{padding-left:20px}.markdown-body img[align=left]{padding-right:20px}.markdown-body .emoji{background-color:initial;max-width:none;vertical-align:text-top}.markdown-body span.frame{display:block;overflow:hidden}.markdown-body span.frame>span{border:1px solid #ddd;display:block;float:left;margin:13px 0 0;overflow:hidden;padding:7px;width:auto}.markdown-body span.frame span img{display:block;float:left}.markdown-body span.frame span span{clear:both;color:#333;display:block;padding:5px 0 0}.markdown-body span.align-center{clear:both;display:block;overflow:hidden}.markdown-body span.align-center>span{display:block;margin:13px auto 0;overflow:hidden;text-align:center}.markdown-body span.align-center span img{margin:0 auto;text-align:center}.markdown-body span.align-right{clear:both;display:block;overflow:hidden}.markdown-body span.align-right>span{display:block;margin:13px 0 0;overflow:hidden;text-align:right}.markdown-body span.align-right span img{margin:0;text-align:right}.markdown-body span.float-left{display:block;float:left;margin-right:13px;overflow:hidden}.markdown-body span.float-left span{margin:13px 0 0}.markdown-body span.float-right{display:block;float:right;margin-left:13px;overflow:hidden}.markdown-body span.float-right>span{display:block;margin:13px auto 0;overflow:hidden;text-align:right}.markdown-body code,.markdown-body tt{background-color:#0000000a;border-radius:3px;font-size:85%;margin:0;padding:.2em 0}.markdown-body code:after,.markdown-body code:before,.markdown-body tt:after,.markdown-body tt:before{content:"\00a0";letter-spacing:-.2em}.markdown-body code br,.markdown-body tt br{display:none}.markdown-body del code{text-decoration:inherit}.markdown-body pre{word-wrap:normal}.markdown-body pre>code{background:#0000;border:0;font-size:100%;margin:0;padding:0;white-space:pre;word-break:normal}.markdown-body .highlight{margin-bottom:16px}.markdown-body .highlight pre{margin-bottom:0;word-break:normal}.markdown-body .highlight pre,.markdown-body pre{background-color:#f7f7f7;border-radius:3px;font-size:85%;line-height:1.45;overflow:auto;padding:16px}.markdown-body pre code,.markdown-body pre tt{word-wrap:normal;background-color:initial;border:0;display:inline;line-height:inherit;margin:0;max-width:auto;overflow:visible;padding:0}.markdown-body pre code:after,.markdown-body pre code:before,.markdown-body pre tt:after,.markdown-body pre tt:before{content:normal}.markdown-body .csv-data td,.markdown-body .csv-data th{font-size:12px;line-height:1;overflow:hidden;padding:5px;text-align:left;white-space:nowrap}.markdown-body .csv-data .blob-line-num{background:#fff;border:0;padding:10px 8px 9px;text-align:right}.markdown-body .csv-data tr{border-top:0}.markdown-body .csv-data th{background:#f8f8f8;border-top:0;font-weight:700}.news .alert .markdown-body blockquote{border:0;padding:0 0 0 40px}.activity-tab .news .alert .commits,.activity-tab .news .markdown-body blockquote{padding-left:0}.task-list-item{list-style-type:none}.task-list-item label{font-weight:400}.task-list-item.enabled label{cursor:pointer}.task-list-item+.task-list-item{margin-top:3px}.task-list-item-checkbox{cursor:default!important;float:left;margin:.31em 0 .2em -1.3em!important;vertical-align:middle}.markdown-body{max-width:758px;overflow:visible!important;padding-bottom:40px;padding-top:40px;position:relative}.markdown-body .emoji{vertical-align:top}.markdown-body pre{border:inherit!important}.markdown-body code{color:inherit!important}.markdown-body pre code .wrapper{display:-moz-inline-flex;display:-ms-inline-flex;display:-o-inline-flex;display:inline-flex}.markdown-body pre code .gutter{float:left;overflow:hidden;-webkit-user-select:none;user-select:none}.markdown-body pre code .gutter.linenumber{border-right:3px solid #6ce26c!important;box-sizing:initial;color:#afafaf!important;cursor:default;display:inline-block;min-width:20px;padding:0 8px 0 0;position:relative;text-align:right;z-index:4}.markdown-body pre code .gutter.linenumber>span:before{content:attr(data-linenumber)}.markdown-body pre code .code{float:left;margin:0 0 0 16px}.markdown-body .gist .line-numbers{border-bottom:none;border-left:none;border-top:none}.markdown-body .gist .line-data{border:none}.markdown-body .gist table{border-collapse:inherit!important;border-spacing:0}.markdown-body code[data-gist-id]{background:none;padding:0}.markdown-body code[data-gist-id]:after,.markdown-body code[data-gist-id]:before{content:""}.markdown-body code[data-gist-id] .blob-num{border:unset}.markdown-body code[data-gist-id] table{margin-bottom:unset;overflow:unset}.markdown-body code[data-gist-id] table tr{background:unset}.markdown-body[dir=rtl] pre{direction:ltr}.markdown-body[dir=rtl] code{direction:ltr;unicode-bidi:embed}.markdown-body .alert>p:last-child{margin-bottom:0}.markdown-body pre.abc,.markdown-body pre.flow-chart,.markdown-body pre.graphviz,.markdown-body pre.mermaid,.markdown-body pre.sequence-diagram,.markdown-body pre.vega{background-color:inherit;border-radius:0;overflow:visible;text-align:center;white-space:inherit}.markdown-body pre.abc>code,.markdown-body pre.flow-chart>code,.markdown-body pre.graphviz>code,.markdown-body pre.mermaid>code,.markdown-body pre.sequence-diagram>code,.markdown-body pre.vega>code{text-align:left}.markdown-body pre.abc>svg,.markdown-body pre.flow-chart>svg,.markdown-body pre.graphviz>svg,.markdown-body pre.mermaid>svg,.markdown-body pre.sequence-diagram>svg,.markdown-body pre.vega>svg{height:100%;max-width:100%}.markdown-body pre>code.wrap{word-wrap:break-word;white-space:pre-wrap;white-space:-moz-pre-wrap;white-space:-pre-wrap;white-space:-o-pre-wrap}.markdown-body .alert>p:last-child,.markdown-body .alert>ul:last-child{margin-bottom:0}.markdown-body summary{display:list-item}.markdown-body summary:focus{outline:none}.markdown-body details summary{cursor:pointer}.markdown-body details:not([open])>:not(summary){display:none}.markdown-body figure{margin:1em 40px}.markdown-body .mark,.markdown-body mark{background-color:#fff1a7}.vimeo,.youtube{background-color:#000;background-position:50%;background-repeat:no-repeat;background-size:contain;cursor:pointer;display:table;overflow:hidden;text-align:center}.vimeo,.youtube{position:relative;width:100%}.youtube{padding-bottom:56.25%}.vimeo img{object-fit:contain;width:100%;z-index:0}.youtube img{object-fit:cover;z-index:0}.vimeo iframe,.youtube iframe,.youtube img{height:100%;left:0;position:absolute;top:0;width:100%}.vimeo iframe,.youtube iframe{vertical-align:middle;z-index:1}.vimeo .icon,.youtube .icon{color:#fff;height:auto;left:50%;opacity:.3;position:absolute;top:50%;transform:translate(-50%,-50%);transition:opacity .2s;width:auto;z-index:0}.vimeo:hover .icon,.youtube:hover .icon{opacity:.6;transition:opacity .2s}.slideshare .inner,.speakerdeck .inner{position:relative;width:100%}.slideshare .inner iframe,.speakerdeck .inner iframe{bottom:0;height:100%;left:0;position:absolute;right:0;top:0;width:100%}.figma{display:table;padding-bottom:56.25%;position:relative;width:100%}.figma iframe{border:1px solid #eee;bottom:0;height:100%;left:0;position:absolute;right:0;top:0;width:100%}.markmap-container{height:300px}.markmap-container>svg{height:100%;width:100%}.MJX_Assistive_MathML{display:none}#MathJax_Message{z-index:1000!important}.ui-infobar{color:#777;margin:25px auto -25px;max-width:760px;position:relative;z-index:2}.toc .invisable-node{list-style-type:none}.ui-toc{bottom:20px;position:fixed;z-index:998}.ui-toc.both-mode{margin-left:8px}.ui-toc.both-mode .ui-toc-label{border-bottom-left-radius:0;border-top-left-radius:0;height:40px;padding:10px 4px}.ui-toc-label{background-color:#e6e6e6;border:none;color:#868686;transition:opacity .2s}.ui-toc .open .ui-toc-label{color:#fff;opacity:1;transition:opacity .2s}.ui-toc-label:focus{background-color:#ccc;color:#000;opacity:.3}.ui-toc-label:hover{background-color:#ccc;opacity:1;transition:opacity .2s}.ui-toc-dropdown{margin-bottom:20px;margin-top:20px;max-height:70vh;max-width:45vw;overflow:auto;padding-left:10px;padding-right:10px;text-align:inherit;width:25vw}.ui-toc-dropdown>.toc{max-height:calc(70vh - 100px);overflow:auto}.ui-toc-dropdown[dir=rtl] .nav{letter-spacing:.0029em;padding-right:0}.ui-toc-dropdown a{overflow:hidden;text-overflow:ellipsis;white-space:pre}.ui-toc-dropdown .nav>li>a{color:#767676;display:block;font-size:13px;font-weight:500;padding:4px 20px}.ui-toc-dropdown .nav>li:first-child:last-child>ul,.ui-toc-dropdown .toc.expand ul{display:block}.ui-toc-dropdown .nav>li>a:focus,.ui-toc-dropdown .nav>li>a:hover{background-color:initial;border-left:1px solid #000;color:#000;padding-left:19px;text-decoration:none}.ui-toc-dropdown[dir=rtl] .nav>li>a:focus,.ui-toc-dropdown[dir=rtl] .nav>li>a:hover{border-left:none;border-right:1px solid #000;padding-right:19px}.ui-toc-dropdown .nav>.active:focus>a,.ui-toc-dropdown .nav>.active:hover>a,.ui-toc-dropdown .nav>.active>a{background-color:initial;border-left:2px solid #000;color:#000;font-weight:700;padding-left:18px}.ui-toc-dropdown[dir=rtl] .nav>.active:focus>a,.ui-toc-dropdown[dir=rtl] .nav>.active:hover>a,.ui-toc-dropdown[dir=rtl] .nav>.active>a{border-left:none;border-right:2px solid #000;padding-right:18px}.ui-toc-dropdown .nav .nav{display:none;padding-bottom:10px}.ui-toc-dropdown .nav>.active>ul{display:block}.ui-toc-dropdown .nav .nav>li>a{font-size:12px;font-weight:400;padding-bottom:1px;padding-left:30px;padding-top:1px}.ui-toc-dropdown[dir=rtl] .nav .nav>li>a{padding-right:30px}.ui-toc-dropdown .nav .nav>li>ul>li>a{font-size:12px;font-weight:400;padding-bottom:1px;padding-left:40px;padding-top:1px}.ui-toc-dropdown[dir=rtl] .nav .nav>li>ul>li>a{padding-right:40px}.ui-toc-dropdown .nav .nav>li>a:focus,.ui-toc-dropdown .nav .nav>li>a:hover{padding-left:29px}.ui-toc-dropdown[dir=rtl] .nav .nav>li>a:focus,.ui-toc-dropdown[dir=rtl] .nav .nav>li>a:hover{padding-right:29px}.ui-toc-dropdown .nav .nav>li>ul>li>a:focus,.ui-toc-dropdown .nav .nav>li>ul>li>a:hover{padding-left:39px}.ui-toc-dropdown[dir=rtl] .nav .nav>li>ul>li>a:focus,.ui-toc-dropdown[dir=rtl] .nav .nav>li>ul>li>a:hover{padding-right:39px}.ui-toc-dropdown .nav .nav>.active:focus>a,.ui-toc-dropdown .nav .nav>.active:hover>a,.ui-toc-dropdown .nav .nav>.active>a{font-weight:500;padding-left:28px}.ui-toc-dropdown[dir=rtl] .nav .nav>.active:focus>a,.ui-toc-dropdown[dir=rtl] .nav .nav>.active:hover>a,.ui-toc-dropdown[dir=rtl] .nav .nav>.active>a{padding-right:28px}.ui-toc-dropdown .nav .nav>.active>.nav>.active:focus>a,.ui-toc-dropdown .nav .nav>.active>.nav>.active:hover>a,.ui-toc-dropdown .nav .nav>.active>.nav>.active>a{font-weight:500;padding-left:38px}.ui-toc-dropdown[dir=rtl] .nav .nav>.active>.nav>.active:focus>a,.ui-toc-dropdown[dir=rtl] .nav .nav>.active>.nav>.active:hover>a,.ui-toc-dropdown[dir=rtl] .nav .nav>.active>.nav>.active>a{padding-right:38px}.markdown-body{font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Helvetica Neue,Helvetica,Roboto,Arial,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol}html[lang^=ja] .markdown-body{font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Helvetica Neue,Helvetica,Roboto,Arial,Hiragino Kaku Gothic Pro,ヒラギノ角ゴ Pro W3,Osaka,Meiryo,メイリオ,MS Gothic,MS ゴシック,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol}html[lang=zh-tw] .markdown-body{font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Helvetica Neue,Helvetica,Roboto,Arial,PingFang TC,Microsoft JhengHei,微軟正黑,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol}html[lang=zh-cn] .markdown-body{font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Helvetica Neue,Helvetica,Roboto,Arial,PingFang SC,Microsoft YaHei,微软雅黑,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol}html .markdown-body[lang^=ja]{font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Helvetica Neue,Helvetica,Roboto,Arial,Hiragino Kaku Gothic Pro,ヒラギノ角ゴ Pro W3,Osaka,Meiryo,メイリオ,MS Gothic,MS ゴシック,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol}html .markdown-body[lang=zh-tw]{font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Helvetica Neue,Helvetica,Roboto,Arial,PingFang TC,Microsoft JhengHei,微軟正黑,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol}html .markdown-body[lang=zh-cn]{font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Helvetica Neue,Helvetica,Roboto,Arial,PingFang SC,Microsoft YaHei,微软雅黑,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol}html[lang^=ja] .ui-toc-dropdown{font-family:Source Sans Pro,Helvetica,Arial,Meiryo UI,MS PGothic,MS Pゴシック,sans-serif}html[lang=zh-tw] .ui-toc-dropdown{font-family:Source Sans Pro,Helvetica,Arial,Microsoft JhengHei UI,微軟正黑UI,sans-serif}html[lang=zh-cn] .ui-toc-dropdown{font-family:Source Sans Pro,Helvetica,Arial,Microsoft YaHei UI,微软雅黑UI,sans-serif}html .ui-toc-dropdown[lang^=ja]{font-family:Source Sans Pro,Helvetica,Arial,Meiryo UI,MS PGothic,MS Pゴシック,sans-serif}html .ui-toc-dropdown[lang=zh-tw]{font-family:Source Sans Pro,Helvetica,Arial,Microsoft JhengHei UI,微軟正黑UI,sans-serif}html .ui-toc-dropdown[lang=zh-cn]{font-family:Source Sans Pro,Helvetica,Arial,Microsoft YaHei UI,微软雅黑UI,sans-serif}.ui-affix-toc{max-height:70vh;max-width:15vw;overflow:auto;position:fixed;top:0}.back-to-top,.expand-toggle,.go-to-bottom{color:#999;display:block;font-size:12px;font-weight:500;margin-left:10px;margin-top:10px;padding:4px 10px}.back-to-top:focus,.back-to-top:hover,.expand-toggle:focus,.expand-toggle:hover,.go-to-bottom:focus,.go-to-bottom:hover{color:#563d7c;text-decoration:none}.back-to-top,.go-to-bottom{margin-top:0}.ui-user-icon{background-position:50%;background-repeat:no-repeat;background-size:cover;border-radius:50%;display:block;height:20px;margin-bottom:2px;margin-right:5px;margin-top:2px;width:20px}.ui-user-icon.small{display:inline-block;height:18px;margin:0 0 .2em;vertical-align:middle;width:18px}.ui-infobar>small>span{line-height:22px}.ui-infobar>small .dropdown{display:inline-block}.ui-infobar>small .dropdown a:focus,.ui-infobar>small .dropdown a:hover{text-decoration:none}.ui-more-info{color:#888;cursor:pointer;vertical-align:middle}.ui-more-info .fa{font-size:16px}.ui-connectedGithub,.ui-published-note{color:#888}.ui-connectedGithub{line-height:23px;white-space:nowrap}.ui-connectedGithub a.file-path{color:#888;padding-left:22px;text-decoration:none}.ui-connectedGithub a.file-path:active,.ui-connectedGithub a.file-path:hover{color:#888;text-decoration:underline}.ui-connectedGithub .fa{font-size:20px}.ui-published-note .fa{font-size:20px;vertical-align:top}.unselectable{-webkit-user-select:none;-o-user-select:none;user-select:none}.selectable{-webkit-user-select:text;-o-user-select:text;user-select:text}.inline-spoiler-section{cursor:pointer}.inline-spoiler-section .spoiler-text{background-color:#333;border-radius:2px}.inline-spoiler-section .spoiler-text>*{opacity:0}.inline-spoiler-section .spoiler-img{filter:blur(10px)}.inline-spoiler-section.raw{background-color:#333;border-radius:2px}.inline-spoiler-section.raw>*{opacity:0}.inline-spoiler-section.unveil{cursor:auto}.inline-spoiler-section.unveil .spoiler-text{background-color:#3333331a}.inline-spoiler-section.unveil .spoiler-text>*{opacity:1}.inline-spoiler-section.unveil .spoiler-img{filter:none}@media print{blockquote,div,img,pre,table{page-break-inside:avoid!important}a[href]:after{font-size:12px!important}}.markdown-body.slides{color:#222;position:relative;z-index:1}.markdown-body.slides:before{background-color:currentColor;bottom:0;box-shadow:0 0 0 50vw;content:"";display:block;left:0;position:absolute;right:0;top:0;z-index:-1}.markdown-body.slides section[data-markdown]{background-color:#fff;margin-bottom:1.5em;position:relative;text-align:center}.markdown-body.slides section[data-markdown] code{text-align:left}.markdown-body.slides section[data-markdown]:before{content:"";display:block;padding-bottom:56.23%}.markdown-body.slides section[data-markdown]>div:first-child{left:1em;max-height:100%;overflow:hidden;position:absolute;right:1em;top:50%;transform:translateY(-50%)}.markdown-body.slides section[data-markdown]>ul{display:inline-block}.markdown-body.slides>section>section+section:after{border:3px solid #777;content:"";height:1.5em;position:absolute;right:1em;top:-1.5em}.site-ui-font{font-family:Source Sans Pro,Helvetica,Arial,sans-serif}html[lang^=ja] .site-ui-font{font-family:Source Sans Pro,Helvetica,Arial,Hiragino Kaku Gothic Pro,ヒラギノ角ゴ Pro W3,Osaka,Meiryo,メイリオ,MS Gothic,MS ゴシック,sans-serif}html[lang=zh-tw] .site-ui-font{font-family:Source Sans Pro,Helvetica,Arial,PingFang TC,Microsoft JhengHei,微軟正黑,sans-serif}html[lang=zh-cn] .site-ui-font{font-family:Source Sans Pro,Helvetica,Arial,PingFang SC,Microsoft YaHei,微软雅黑,sans-serif}body{font-smoothing:subpixel-antialiased!important;-webkit-font-smoothing:subpixel-antialiased!important;-moz-osx-font-smoothing:auto!important;-webkit-overflow-scrolling:touch;font-family:Source Sans Pro,Helvetica,Arial,sans-serif;letter-spacing:.025em}html[lang^=ja] body{font-family:Source Sans Pro,Helvetica,Arial,Hiragino Kaku Gothic Pro,ヒラギノ角ゴ Pro W3,Osaka,Meiryo,メイリオ,MS Gothic,MS ゴシック,sans-serif}html[lang=zh-tw] body{font-family:Source Sans Pro,Helvetica,Arial,PingFang TC,Microsoft JhengHei,微軟正黑,sans-serif}html[lang=zh-cn] body{font-family:Source Sans Pro,Helvetica,Arial,PingFang SC,Microsoft YaHei,微软雅黑,sans-serif}abbr[title]{border-bottom:none;text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted}abbr[data-original-title],abbr[title]{cursor:help}body.modal-open{overflow-y:auto;padding-right:0!important}svg{text-shadow:none}
  23. </style>
  24. <!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
  25. <!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
  26. <!--[if lt IE 9]>
  27. <script src="https://cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv.min.js" integrity="sha256-3Jy/GbSLrg0o9y5Z5n1uw0qxZECH7C6OQpVBgNFYa0g=" crossorigin="anonymous"></script>
  28. <script src="https://cdnjs.cloudflare.com/ajax/libs/respond.js/1.4.2/respond.min.js" integrity="sha256-g6iAfvZp+nDQ2TdTR/VVKJf3bGro4ub5fvWSWVRi2NE=" crossorigin="anonymous"></script>
  29. <script src="https://cdnjs.cloudflare.com/ajax/libs/es5-shim/4.5.9/es5-shim.min.js" integrity="sha256-8E4Is26QH0bD52WoQpcB+R/tcWQtpzlCojrybUd7Mxo=" crossorigin="anonymous"></script>
  30. <![endif]-->
  31. </head>
  32. <body>
  33. <div id="doc" class="markdown-body container-fluid comment-enabled" data-hard-breaks="true"><h1 id="Lab-7-Scheduling-tasks" data-id="Lab-7-Scheduling-tasks"><a class="anchor hidden-xs" href="#Lab-7-Scheduling-tasks" title="Lab-7-Scheduling-tasks"><span class="octicon octicon-link"></span></a><span>Lab 7: Scheduling tasks</span></h1><h3 id="Task-1-Create-cron-jobs" data-id="Task-1-Create-cron-jobs"><a class="anchor hidden-xs" href="#Task-1-Create-cron-jobs" title="Task-1-Create-cron-jobs"><span class="octicon octicon-link"></span></a><span>Task 1: Create cron jobs</span></h3><ul>
  34. <li><span>Let’s create an example script </span><code>job.sh</code><span>. We can check </span><code>log.txt</code><span> at any time to see whether our scheduled job has run. Create this script in your home directory</span><pre><code class="bash hljs"><div class="wrapper"><div class="gutter linenumber"><span></span>
  35. <span></span></div><div class="code"><span class="hljs-meta">#!/bin/bash</span>
  36. <span class="hljs-built_in">echo</span> `<span class="hljs-built_in">date</span> +<span class="hljs-string">"%Y-%M-%d %T"</span>`<span class="hljs-string">" - Hello world"</span> &gt;&gt; /var/log/log.txt
  37. </div></div></code></pre>
  38. </li>
  39. </ul><p><strong><span>Adding the job to the user crontab</span></strong></p><ul>
  40. <li><span>To understand the user crontab, let’s add the script to it manually</span><pre><code>$ crontab -e
  41. </code></pre>
  42. </li>
  43. <li><span>This command will open an editor to edit the existing user crontab. Let’s append our cron expression:</span>
  44. <blockquote>
  45. <p><span>Replace the </span><code>&lt;username&gt;</code><span> with your username</span></p>
  46. </blockquote>
  47. <pre><code>30 0 * * * /home/&lt;username&gt;/job.sh
  48. </code></pre>
  49. <span>This schedules the script to run every day, 30 minutes after midnight.</span></li>
  50. <li><span>We also need to be sure that the current user has execute permissions for this script. So, let’s use the chmod command to add them:</span><pre><code>$ chmod u+x /home/&lt;username&gt;/job.sh
  51. </code></pre>
  52. <span>Now, </span><code>job.sh</code><span> is scheduled and will run every day. We can test this by inspecting the </span><code>log.txt</code><span> file</span></li>
  53. </ul><p><strong><span>Adding the job to the system crontab</span></strong><br>
  54. <span>To understand the system crontab, let’s also add this script to it manually.</span></p><ul>
  55. <li><span>The system crontab file is kept in </span><code>/etc/crontab</code><span>. Let’s append the following line:</span><pre><code>30 0 * * * root /home/&lt;username&gt;/job.sh
  56. </code></pre>
  57. <span>We should note that we need to specify the root username. This is because jobs in system cron are system jobs and will be run by the root user.</span></li>
  58. </ul><p><strong><span>Script for adding the job to the user crontab</span></strong><br>
  59. <span>Now let’s try automating the process to add to the user crontab. Install a new file to crontab.</span></p><ul>
  60. <li><span>Let’s first create a new script file:</span><pre><code>$ touch /home/&lt;username&gt;/myScript.sh
  61. </code></pre>
  62. </li>
  63. <li><span>The first thing our script will do is take a copy of all the current jobs. Do not forget to add executable rights for the script to work</span><pre><code class="bash hljs"><div class="wrapper"><div class="gutter linenumber"><span></span>
  64. <span></span></div><div class="code"><span class="hljs-meta">#!/bin/bash</span>
  65. crontab -l &gt; crontab_new
  66. </div></div></code></pre>
  67. <span>We now have all the previous jobs in the </span><code>crontab_new</code><span> file. This means we can append our new job to it and then rewrite the crontab by using the edited file as an input argument to the crontab command:</span><pre><code>$ echo "30 0 * * * /home/&lt;username&gt;/job.sh" &gt;&gt; crontab_new
  68. $ crontab crontab_new
  69. </code></pre>
  70. </li>
  71. <li><span>Since the </span><code>crontab_new</code><span> file is temporary, we can remove it:</span><pre><code>$ rm crontab_new
  72. </code></pre>
  73. <span>This method works well, though it does require the use of a temporary file. The main idea here is to add multiple tasks to the existing user jobs. Let’s see if we can optimize it further.</span></li>
  74. </ul><p><strong><span>Optimize the previous script by using a pipe</span></strong><br>
  75. <span>Our previous script relied on a temporary file and had to tidy it up. It also didn’t check whether the cron entry was already installed, and thus, it could install a duplicate entry if executed multiple times.</span></p><ul>
  76. <li><span>We can address both of these by using a pipe-based script. If crontab command has a dash </span><code>-</code><span>, the crontab data is read from standard the input</span><pre><code class="bash hljs"><div class="wrapper"><div class="gutter linenumber"><span></span>
  77. <span></span></div><div class="code"><span class="hljs-meta">#!/bin/bash</span>
  78. (crontab -l; <span class="hljs-built_in">echo</span> <span class="hljs-string">"30 0 * * * /home/&lt;username&gt;/job.sh"</span>) | <span class="hljs-built_in">sort</span> -u | crontab -
  79. </div></div></code></pre>
  80. <span>As before, the </span><code>crontab -l</code><span> and </span><code>echo</code><span> commands write out the previous lines of the crontab as well as the new entry. These are piped through the sort command to remove duplicate lines. The </span><code>-u</code><span> option in </span><code>sort</code><span> is for keeping only unique lines.</span><br>
  81. <span>The result of this is piped into the </span><code>crontab</code><span> command, which rewrites the crontab file with the new entries.</span><br>
  82. <span>We should be aware, though, that using sort will completely reorder the file, including any comments. </span><code>sort -u</code><span> is pretty easy to understand in a script, but we can achieve a less destructive de-duplication with awk:</span></li>
  83. </ul><pre><code class="bash hljs"><div class="wrapper"><div class="gutter linenumber"><span></span>
  84. <span></span></div><div class="code"><span class="hljs-meta">#!/bin/bash</span>
  85. (crontab -l; <span class="hljs-built_in">echo</span> <span class="hljs-string">"30 0 * * * /home/&lt;username&gt;/job.sh"</span>)|awk <span class="hljs-string">'!x[$0]++'</span>|crontab -
  86. </div></div></code></pre><p><span>This will remove all duplicates from the crontab without sorting it.</span></p><ul>
  87. <li><span>The syntax of the awk command used is explained below:</span>
  88. <ul>
  89. <li><code>a[$0]</code><span> - uses the current line </span><code>$0</code><span> as key to the array, taking the value stored there. If this particular key was never referenced before, </span><code>a[$0]</code><span> evaluates to the empty string.</span></li>
  90. <li><span>The </span><code>!</code><span> negates the value from before. If it was empty or zero (false), we now have a true result. If it was non-zero (true), we have a false result. If the whole expression evaluated to true, the whole line is printed as the default action print </span><code>$0</code></li>
  91. <li><code>++</code><span> increment the value of </span><code>a[$0]</code></li>
  92. </ul>
  93. </li>
  94. </ul><p><strong><span>Using system crontab</span></strong></p><ul>
  95. <li><span>First, let’s create a new script:</span><pre><code>$ touch /home/$USER/myScript2.sh
  96. </code></pre>
  97. <span>The syntax of the system schedule line is similar to the user schedule. We just need to specify the root username in the schedule line</span><pre><code class="bash hljs"><div class="wrapper"><div class="gutter linenumber"><span></span>
  98. <span></span></div><div class="code"><span class="hljs-meta">#!/bin/bash</span>
  99. sudo /bin/bash -c <span class="hljs-string">'echo "30 0 * * * root /home/&lt;username&gt;/job.sh" &gt;&gt; /etc/crontab'</span>
  100. </div></div></code></pre>
  101. <span>We’re using </span><code>sudo /bin/bash</code><span> before </span><code>echo</code><span> because the user needs root access to both echo and redirect as the root user. Otherwise, we’ll get a permission denied error because just echo will run as root and the redirection will be made with the current user’s permission. The </span><code>-c</code><span> option tells bash to get the command in single quotes as a string and run it in a shell.</span><br>
  102. <span>Note that this is plain file manipulation, compared with the </span><code>crontab</code><span> command used earlier. We can add similar filters like sort or awk if we want to avoid duplicate entries.</span></li>
  103. </ul><p><strong><span>Using the /etc/cron.d directory</span></strong><br>
  104. <span>Besides the </span><code>/etc/crontab</code><span> path, cron considers all the files in the </span><code>/etc/cron.d</code><span> directory as system jobs too. So, we can also put the schedule line in a new file in the </span><code>/etc/cron.d</code><span> directory.</span></p><ul>
  105. <li>
  106. <p><span>Let’s now make another script for adding a job to the </span><code>cron.d</code><span> directory, as an alternative to the </span><code>/etc/crontab</code><span> file:</span></p>
  107. <pre><code>$ touch /home/&lt;username&gt;/myScript3.sh
  108. </code></pre>
  109. <p><span>We need to put the schedule line in a new file in the cron.d directory — we’ll call our file schedule. Note that in </span><code>/etc/cron.d</code><span>, some filenames are considered invalid. For example, if we choose </span><code>schedule.sh</code><span> for the filename, it will be skipped because the filename should not have any extension:</span></p>
  110. <pre><code class="hljs"><div class="wrapper"><div class="gutter linenumber"><span></span>
  111. <span></span></div><div class="code">#!/bin/bash
  112. sudo touch /etc/cron.d/schedule
  113. </div></div></code></pre>
  114. <p><span>The </span><code>cron.d</code><span> directory and its sub-directories are usually used by system services, and only the root user can have access to these directories. Also, the files in </span><code>/etc/cron.d</code><span> must be owned by root. So, we need to use sudo.</span></p>
  115. </li>
  116. <li>
  117. <p><span>Let’s now add our schedule line to the schedule file and change the permissions.</span></p>
  118. <pre><code>$ sudo /bin/bash -c 'echo "30 0 * * * root /home/&lt;username&gt;/job.sh" &gt; /etc/cron.d/schedule'
  119. $ sudo chmod 600 /etc/cron.d/schedule
  120. </code></pre>
  121. <p><span>Note that we change the file’s permissions to a minimum </span><code>600</code><span>. This is because files in </span><code>/etc/cron.d</code><span> must not be writable by group or other. Otherwise, they will be ignored. Also, the schedule files under </span><code>/etc/cron.d</code><span> do not need to be executable. So, we don’t need permission </span><code>700</code><span>.</span></p>
  122. </li>
  123. </ul><h1 id="Task-2-at-command" data-id="Task-2-at-command"><a class="anchor hidden-xs" href="#Task-2-at-command" title="Task-2-at-command"><span class="octicon octicon-link"></span></a><span>Task 2: </span><code>at</code><span> command</span></h1><p><code>at</code><span> is a command-line utility that allows you to schedule commands to be executed at a particular time. Jobs created with </span><code>at</code><span> are executed only once.</span></p><ul>
  124. <li><span>Install </span><code>at</code><span> from the repository</span><pre><code>$ sudo apt install at
  125. </code></pre>
  126. </li>
  127. <li><span>Once the program is installed make sure </span><code>atd</code><span>, the scheduling daemon is running and set to start on boot:</span><pre><code>$ sudo systemctl enable --now atd
  128. </code></pre>
  129. </li>
  130. <li><span>The simplified syntax for the at command is as follows:</span><pre><code>$ at [OPTION...] runtime
  131. </code></pre>
  132. </li>
  133. <li><span>Let’s create a job that will be executed at 9:00 am:</span><pre><code>$ at 09:00
  134. </code></pre>
  135. <span>Once you hit Enter, you’ll be presented with the </span><code>at</code><span> command prompt that most often starts with </span><code>at&gt;</code><span>. You also see a warning that tells you the shell in which the command will run:</span><pre><code>warning: commands will be executed using /bin/sh
  136. at&gt;
  137. </code></pre>
  138. <span>Enter one or more command you want to execute:</span><pre><code>at&gt; tar -xf $HOME/file.tar.gz
  139. </code></pre>
  140. <span>When you’re done entering the commands, press </span><code>CTRL+D</code><span> to exit the prompt and save the job:</span><pre><code>at&gt; &lt;EOT&gt;
  141. job 1 at Mon Oct 17 09:00:00 2022
  142. </code></pre>
  143. </li>
  144. <li><span>There are also other ways to pass the command you want to run, besides entering the command in the at prompt. One way is to use echo and pipe the command to at:</span><pre><code>$ echo "command_to_be_run" | at 09:00
  145. </code></pre>
  146. </li>
  147. <li><span>Another option is to use the redirect</span><pre><code>$ at 09:00 &lt;&lt;END
  148. command_to_be_run
  149. END
  150. </code></pre>
  151. </li>
  152. <li><span>To read the commands from a file instead of the standard input, invoke the command with -f option following by the path to the file. For example, to create a job that will run the script </span><code>$HOME/script.sh</code><span>:</span><pre><code>$ at 09:00 -f $HOME/script.sh
  153. </code></pre>
  154. </li>
  155. </ul><h2 id="Questions-to-answer" data-id="Questions-to-answer"><a class="anchor hidden-xs" href="#Questions-to-answer" title="Questions-to-answer"><span class="octicon octicon-link"></span></a><span>Questions to answer</span></h2><blockquote>
  156. <p><span>Upload the scripts you create to moodle.</span><br>
  157. <span>Show test results of all implementation in your report.</span></p>
  158. </blockquote><ol>
  159. <li>
  160. <p><span>Create backup for any directory with files inside.</span></p>
  161. <ul>
  162. <li><span>Create a cron job which backs up the directories at the 5th day of every month.</span></li>
  163. <li><span>Create an anacron job that backs up the directories daily. The anacron job should delete old backups.</span></li>
  164. </ul>
  165. </li>
  166. <li>
  167. <p><span>Install nginx and create a cron job that backs up the directory that contains </span><code>index.html</code><span>.</span></p>
  168. <ul>
  169. <li><span>The backup should occur at midnight every Sunday.</span></li>
  170. <li><span>The job should delete old or previous backups.</span></li>
  171. </ul>
  172. </li>
  173. <li>
  174. <p><span>Create cron jobs that appends the current time and a descriptive information about the job to the log file at </span><code>/var/log/sna_cron.log</code><span>. You should meet the following requirements:</span></p>
  175. <ul>
  176. <li><span>Use </span><code>/bin/bash</code><span> to run commands instead of the default </span><code>/bin/sh</code></li>
  177. <li><span>Schedule the following jobs:</span>
  178. <ul>
  179. <li><span>Run five minutes after midnight, everyday</span></li>
  180. <li><span>Run at 10:00 on weekdays</span></li>
  181. <li><span>Run at 04:00 every Monday</span></li>
  182. <li><span>Run on the second saturday of every month.</span></li>
  183. </ul>
  184. </li>
  185. </ul>
  186. <blockquote>
  187. <p><span>Example output written to the log file when the first job executes is shown below</span></p>
  188. <pre><code>14-10-22 00:05:00 Run five minutes after midnight
  189. </code></pre>
  190. </blockquote>
  191. </li>
  192. </ol><h3 id="Bonus" data-id="Bonus"><a class="anchor hidden-xs" href="#Bonus" title="Bonus"><span class="octicon octicon-link"></span></a><span>Bonus</span></h3><ol start="4">
  193. <li><span>How can cron jobs be abused?</span>
  194. <ul>
  195. <li><span>Give one specific real life example where cron job was abused.</span></li>
  196. <li><span>Provide details about the job that was scheduled which led to the abuse. Details should include job execution frequency, command/script scheduled, and the objective(s) of the job.</span></li>
  197. <li><span>Show the job you are describing. For example </span><code>* * * * * /var/tmp/.ICE-unix/-l/sh &gt;/dev/null 2&gt;&amp;1</code>
  198. <blockquote>
  199. <p><span>Be brief in your explanations. Answer this question with a maximum of six sentences.</span></p>
  200. </blockquote>
  201. </li>
  202. </ul>
  203. </li>
  204. </ol></div>
  205. <div class="ui-toc dropup unselectable hidden-print" style="display:none;">
  206. <div class="pull-right dropdown">
  207. <a id="tocLabel" class="ui-toc-label btn btn-default" data-toggle="dropdown" href="#" role="button" aria-haspopup="true" aria-expanded="false" title="Table of content">
  208. <i class="fa fa-bars"></i>
  209. </a>
  210. <ul id="ui-toc" class="ui-toc-dropdown dropdown-menu" aria-labelledby="tocLabel">
  211. <div class="toc"><ul class="nav">
  212. <li class=""><a href="#Lab-7-Scheduling-tasks" title="Lab 7: Scheduling tasks">Lab 7: Scheduling tasks</a><ul class="nav">
  213. <li class="invisable-node"><ul class="nav">
  214. <li class=""><a href="#Task-1-Create-cron-jobs" title="Task 1: Create cron jobs">Task 1: Create cron jobs</a></li>
  215. </ul>
  216. </li>
  217. </ul>
  218. </li>
  219. <li class=""><a href="#Task-2-at-command" title="Task 2: at command">Task 2: at command</a><ul class="nav">
  220. <li class=""><a href="#Questions-to-answer" title="Questions to answer">Questions to answer</a><ul class="nav">
  221. <li class=""><a href="#Bonus" title="Bonus">Bonus</a></li>
  222. </ul>
  223. </li>
  224. </ul>
  225. </li>
  226. </ul>
  227. </div><div class="toc-menu"><a class="expand-toggle" href="#">Expand all</a><a class="back-to-top" href="#">Back to top</a><a class="go-to-bottom" href="#">Go to bottom</a></div>
  228. </ul>
  229. </div>
  230. </div>
  231. <div id="ui-toc-affix" class="ui-affix-toc ui-toc-dropdown unselectable hidden-print" data-spy="affix" style="top:17px;display:none;" null null>
  232. <div class="toc"><ul class="nav">
  233. <li class=""><a href="#Lab-7-Scheduling-tasks" title="Lab 7: Scheduling tasks">Lab 7: Scheduling tasks</a><ul class="nav">
  234. <li class="invisable-node"><ul class="nav">
  235. <li class=""><a href="#Task-1-Create-cron-jobs" title="Task 1: Create cron jobs">Task 1: Create cron jobs</a></li>
  236. </ul>
  237. </li>
  238. </ul>
  239. </li>
  240. <li class=""><a href="#Task-2-at-command" title="Task 2: at command">Task 2: at command</a><ul class="nav">
  241. <li class=""><a href="#Questions-to-answer" title="Questions to answer">Questions to answer</a><ul class="nav">
  242. <li class=""><a href="#Bonus" title="Bonus">Bonus</a></li>
  243. </ul>
  244. </li>
  245. </ul>
  246. </li>
  247. </ul>
  248. </div><div class="toc-menu"><a class="expand-toggle" href="#">Expand all</a><a class="back-to-top" href="#">Back to top</a><a class="go-to-bottom" href="#">Go to bottom</a></div>
  249. </div>
  250. <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.min.js" integrity="sha256-hVVnYaiADRTO2PzUGmuLJr8BLUSjGIZsDYGmIJLv2b8=" crossorigin="anonymous"></script>
  251. <script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha256-U5ZEeKfGNOja007MMD3YBI0A3OSZOQbeG6z2f2Y0hu8=" crossorigin="anonymous" defer></script>
  252. <script src="https://cdnjs.cloudflare.com/ajax/libs/gist-embed/2.6.0/gist-embed.min.js" integrity="sha256-KyF2D6xPIJUW5sUDSs93vWyZm+1RzIpKCexxElmxl8g=" crossorigin="anonymous" defer></script>
  253. <script>
  254. var markdown = $(".markdown-body");
  255. //smooth all hash trigger scrolling
  256. function smoothHashScroll() {
  257. var hashElements = $("a[href^='#']").toArray();
  258. for (var i = 0; i < hashElements.length; i++) {
  259. var element = hashElements[i];
  260. var $element = $(element);
  261. var hash = element.hash;
  262. if (hash) {
  263. $element.on('click', function (e) {
  264. // store hash
  265. var hash = this.hash;
  266. if ($(hash).length <= 0) return;
  267. // prevent default anchor click behavior
  268. e.preventDefault();
  269. // animate
  270. $('body, html').stop(true, true).animate({
  271. scrollTop: $(hash).offset().top
  272. }, 100, "linear", function () {
  273. // when done, add hash to url
  274. // (default click behaviour)
  275. window.location.hash = hash;
  276. });
  277. });
  278. }
  279. }
  280. }
  281. smoothHashScroll();
  282. var toc = $('.ui-toc');
  283. var tocAffix = $('.ui-affix-toc');
  284. var tocDropdown = $('.ui-toc-dropdown');
  285. //toc
  286. tocDropdown.click(function (e) {
  287. e.stopPropagation();
  288. });
  289. var enoughForAffixToc = true;
  290. function generateScrollspy() {
  291. $(document.body).scrollspy({
  292. target: ''
  293. });
  294. $(document.body).scrollspy('refresh');
  295. if (enoughForAffixToc) {
  296. toc.hide();
  297. tocAffix.show();
  298. } else {
  299. tocAffix.hide();
  300. toc.show();
  301. }
  302. $(document.body).scroll();
  303. }
  304. function windowResize() {
  305. //toc right
  306. var paddingRight = parseFloat(markdown.css('padding-right'));
  307. var right = ($(window).width() - (markdown.offset().left + markdown.outerWidth() - paddingRight));
  308. toc.css('right', right + 'px');
  309. //affix toc left
  310. var newbool;
  311. var rightMargin = (markdown.parent().outerWidth() - markdown.outerWidth()) / 2;
  312. //for ipad or wider device
  313. if (rightMargin >= 133) {
  314. newbool = true;
  315. var affixLeftMargin = (tocAffix.outerWidth() - tocAffix.width()) / 2;
  316. var left = markdown.offset().left + markdown.outerWidth() - affixLeftMargin;
  317. tocAffix.css('left', left + 'px');
  318. } else {
  319. newbool = false;
  320. }
  321. if (newbool != enoughForAffixToc) {
  322. enoughForAffixToc = newbool;
  323. generateScrollspy();
  324. }
  325. }
  326. $(window).resize(function () {
  327. windowResize();
  328. });
  329. $(document).ready(function () {
  330. windowResize();
  331. generateScrollspy();
  332. });
  333. //remove hash
  334. function removeHash() {
  335. window.location.hash = '';
  336. }
  337. var backtotop = $('.back-to-top');
  338. var gotobottom = $('.go-to-bottom');
  339. backtotop.click(function (e) {
  340. e.preventDefault();
  341. e.stopPropagation();
  342. if (scrollToTop)
  343. scrollToTop();
  344. removeHash();
  345. });
  346. gotobottom.click(function (e) {
  347. e.preventDefault();
  348. e.stopPropagation();
  349. if (scrollToBottom)
  350. scrollToBottom();
  351. removeHash();
  352. });
  353. var toggle = $('.expand-toggle');
  354. var tocExpand = false;
  355. checkExpandToggle();
  356. toggle.click(function (e) {
  357. e.preventDefault();
  358. e.stopPropagation();
  359. tocExpand = !tocExpand;
  360. checkExpandToggle();
  361. })
  362. function checkExpandToggle () {
  363. var toc = $('.ui-toc-dropdown .toc');
  364. var toggle = $('.expand-toggle');
  365. if (!tocExpand) {
  366. toc.removeClass('expand');
  367. toggle.text('Expand all');
  368. } else {
  369. toc.addClass('expand');
  370. toggle.text('Collapse all');
  371. }
  372. }
  373. function scrollToTop() {
  374. $('body, html').stop(true, true).animate({
  375. scrollTop: 0
  376. }, 100, "linear");
  377. }
  378. function scrollToBottom() {
  379. $('body, html').stop(true, true).animate({
  380. scrollTop: $(document.body)[0].scrollHeight
  381. }, 100, "linear");
  382. }
  383. </script>
  384. </body>
  385. </html>