├── .editorconfig
├── .gitattributes
├── .github
└── workflows
│ ├── lock-closed.yml
│ └── stale.yml
├── .gitignore
├── CONTRIBUTING.md
├── DEVELOPMENT.md
├── LICENSE.md
├── MANIFEST.in
├── README.md
├── WHATSNEW.md
├── babel.cfg
├── gulpfile.js
├── octoprint_touchui
├── __init__.py
├── api.py
├── customization.py
├── decorators.py
├── static
│ ├── README.md
│ ├── css
│ │ ├── .gitignore
│ │ └── touchui.css
│ ├── fonts
│ │ ├── fontawesome
│ │ │ ├── FontAwesome.otf
│ │ │ ├── fontawesome-webfont.eot
│ │ │ ├── fontawesome-webfont.svg
│ │ │ ├── fontawesome-webfont.ttf
│ │ │ ├── fontawesome-webfont.woff
│ │ │ └── fontawesome-webfont.woff2
│ │ ├── sourcecodepro
│ │ │ ├── LICENSE.txt
│ │ │ ├── SourceCodePro-Bold.woff
│ │ │ ├── SourceCodePro-Light.woff
│ │ │ ├── SourceCodePro-Regular.woff
│ │ │ └── SourceCodePro-Semibold.woff
│ │ └── touchui
│ │ │ ├── touchui.eot
│ │ │ ├── touchui.svg
│ │ │ ├── touchui.ttf
│ │ │ └── touchui.woff
│ ├── img
│ │ └── colorpicker.png
│ ├── js
│ │ ├── touchui.bootstrap.js
│ │ ├── touchui.bundled.js
│ │ └── touchui.libraries.js
│ └── less
│ │ └── touchui.bundled.less
└── templates
│ ├── empty.jinja2
│ ├── touchui_load_css.jinja2
│ ├── touchui_menu_item.jinja2
│ ├── touchui_modal.jinja2
│ └── touchui_settings.jinja2
├── package-lock.json
├── package.json
├── requirements.txt
├── setup.py
└── source
├── js
├── animate
│ └── hide.js
├── bootstrap.js
├── components
│ ├── dropdown.js
│ ├── fullscreen.js
│ ├── keyboard.js
│ ├── modal.js
│ ├── slider.js
│ ├── touch-list.js
│ └── touchscreen.js
├── constructor.js
├── core
│ ├── _init.js
│ ├── boot.js
│ ├── bridge.js
│ ├── less.js
│ └── version.js
├── dom
│ ├── _init.js
│ ├── cookies.js
│ ├── create
│ │ ├── dropdown.js
│ │ ├── printer.js
│ │ ├── tabbar.js
│ │ └── webcam.js
│ ├── localstorage.js
│ ├── move
│ │ ├── afterTabAndNav.js
│ │ ├── connection.js
│ │ ├── controls.js
│ │ ├── navbar.js
│ │ ├── overlays.js
│ │ ├── sidebar.js
│ │ ├── tabbar.js
│ │ └── terminal.js
│ ├── overwrite
│ │ ├── modal.js
│ │ ├── pnotify.js
│ │ ├── tabbar.js
│ │ └── tabdrop.js
│ └── storage.js
├── knockout
│ ├── bindings.js
│ ├── isLoading.js
│ ├── isReady.js
│ └── viewModel.js
├── plugins
│ ├── _init.js
│ ├── disable.js
│ ├── multiwebcam.js
│ ├── psucontrol.js
│ └── screensquish.js
└── scroll
│ ├── _beforeLoad.js
│ ├── _init.js
│ ├── block-events.js
│ ├── body.js
│ ├── modal.js
│ ├── overlay.js
│ ├── overwrite.js
│ └── terminal.js
├── less
├── _mixins.less
├── _variables.less
├── components
│ ├── dropdown.less
│ ├── emulate.touch.less
│ ├── files.less
│ ├── keyboard.less
│ ├── modal.less
│ ├── notifications.less
│ ├── overlays.less
│ ├── pagination.less
│ ├── popover.less
│ ├── progress.less
│ ├── scroll.less
│ ├── scrollbars.less
│ └── tinycolorpicker.less
├── fonts
│ ├── fontawesome.less
│ ├── sourcecodepro.less
│ └── touchui.less
├── layout
│ ├── buttons.less
│ ├── footer.less
│ ├── form.less
│ ├── general.less
│ ├── header.less
│ ├── links.less
│ ├── navbar.less
│ ├── tabbar.less
│ └── touchscreen.less
├── plugins
│ ├── eeprommarlin.less
│ ├── history.less
│ ├── m33fio.less
│ ├── multicam.less
│ ├── navbartemp.less
│ ├── octolapse.less
│ ├── printerstatistics.less
│ └── psucontrol.less
├── settings
│ └── touchui.less
├── tabs
│ ├── connection.less
│ ├── controls.less
│ ├── gcode.less
│ ├── printer.less
│ ├── temperature.less
│ ├── terminal.less
│ ├── timelapse.less
│ └── webcam.less
└── touchui.less
├── svg
├── C.svg
├── H.svg
├── O.svg
├── T.svg
├── U.svg
├── UI.svg
└── fan.svg
└── vendors
└── tinycolorpicker
└── lib
├── jquery.tinycolorpicker.js
├── jquery.tinycolorpicker.min.js
├── tinycolorpicker.js
└── tinycolorpicker.min.js
/.editorconfig:
--------------------------------------------------------------------------------
1 | # This file is for unifying the coding style for different editors and IDEs
2 | # editorconfig.org
3 |
4 | root = true
5 |
6 | [*]
7 | end_of_line = lf
8 | charset = utf-8
9 | insert_final_newline = true
10 | trim_trailing_whitespace = true
11 | indent_style = tab
12 | indent_size = 4
13 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | octoprint_touchui/static/css/*.css binary
2 | octoprint_touchui/static/less/touchui.*.less binary
3 | octoprint_touchui/static/js/touchui.*.js binary
4 |
--------------------------------------------------------------------------------
/.github/workflows/lock-closed.yml:
--------------------------------------------------------------------------------
1 | name: Lock Closed Issues
2 |
3 | on:
4 | schedule:
5 | - cron: '0 1/13 * * *'
6 |
7 | jobs:
8 | lock:
9 | name: Lock Closed Issues
10 | if: github.repository == 'BillyBlaze/OctoPrint-TouchUI'
11 |
12 | runs-on: ubuntu-latest
13 |
14 | steps:
15 | - uses: dessant/lock-threads@v2
16 | with:
17 | github-token: ${{ secrets.GITHUB_TOKEN }}
18 | process-only: 'issues'
19 | issue-lock-inactive-days: '14'
20 | issue-exclude-created-before: ''
21 | issue-exclude-labels: 'no-locking'
22 | issue-lock-labels: ''
23 | issue-lock-comment: >
24 | This issue has been automatically locked since there
25 | has not been any recent activity after it was closed.
26 | Please open a new issue for related bugs.
27 | issue-lock-reason: ''
28 |
--------------------------------------------------------------------------------
/.github/workflows/stale.yml:
--------------------------------------------------------------------------------
1 | name: Mark stale issues and pull requests
2 |
3 | on:
4 | schedule:
5 | - cron: "30 1 * * *"
6 |
7 | jobs:
8 | stale:
9 |
10 | runs-on: ubuntu-latest
11 |
12 | steps:
13 | - uses: actions/stale@v1
14 | with:
15 | repo-token: ${{ secrets.GITHUB_TOKEN }}
16 | days-before-stale: 14
17 | days-before-close: 7
18 | stale-issue-message: >
19 | This issue has been automatically marked as inactive because it has not had
20 | recent activity. It will be closed if no further activity occurs. Thank you
21 | for your contributions.
22 | stale-pr-message: >
23 | This issue has been automatically marked as inactive because it has not had
24 | recent activity. It will be closed if no further activity occurs. Thank you
25 | for your contributions.
26 | stale-issue-label: 'inactive'
27 | stale-pr-label: 'inactive'
28 | exempt-issue-labels: 'help wanted,up for grabs,fixed,confirmed,pending'
29 | exempt-pr-labels: 'help wanted,up for grabs,fixed,confirmed,pending'
30 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.pyc
2 | *.swp
3 | .idea
4 | *.iml
5 | build
6 | dist
7 | *.egg*
8 | .DS_Store
9 | *.zip
10 | node_modules
11 | .vscode/
12 | octoprint_touchui/WHATSNEW.md
13 | !source/vendors/tinycolorpicker/*
14 | source/vendors/
15 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing to TouchUI
2 |
3 | :+1::tada: First off, thanks for taking the time to contribute! :tada::+1:
4 |
5 | ## How Can I Contribute?
6 |
7 | ### Reporting Bugs
8 |
9 | This section guides you through submitting a bug report for TouchUI. Following these guidelines helps maintainers and the community understand your report :pencil:, reproduce the behavior :computer: :computer:, and find related reports :mag_right:.
10 |
11 | Before creating bug reports, please check [this list](#before-submitting-a-bug-report) as you might find out that you don't need to create one. When you are creating a bug report, please [include as many details as possible](#how-do-i-submit-a-good-bug-report). If you'd like, you can use [this template](#template-for-submitting-bug-reports) to structure the information.
12 |
13 | #### OctoPrint and plugins
14 | Sometimes bugs can be related to a specific plugin or OctoPrint version. Since TouchUI extends the code base of OctoPrint, a bug can also exist in the default interface.
15 | * Check if the bug also exists in the default interface. If yes, report this problem to [OctoPrint](https://github.com/foosel/OctoPrint).
16 | * Check if you have installed a plugin that interferes with TouchUI or your bug. If you do; try to disable it. If it has no effect, add this to your bug report.
17 |
18 | #### Before Submitting A Bug Report
19 |
20 | * **Check the [Troubleshooting on the Wiki](https://github.com/BillyBlaze/OctoPrint-TouchUI/wiki/Setup:-Troubleshooting)** for a list of common questions and problems.
21 | * **Perform a [cursory search](https://github.com/BillyBlaze/OctoPrint-TouchUI/issues?utf8=%E2%9C%93&q=)** to see if the problem has already been reported. If it has, add a comment to the existing issue instead of opening a new one.
22 |
23 | #### How Do I Submit A (Good) Bug Report?
24 |
25 | Bugs are tracked as [GitHub issues](https://guides.github.com/features/issues/). After you've determined [which repository](#octoprint-and-plugins) your bug is related to, create an issue on that repository and provide the following information.
26 |
27 | Explain the problem and include additional details to help maintainers reproduce the problem:
28 |
29 | * **Which version of OctoPrint and TouchUI are you using**?
30 | * **What's the name and version of the Browser you're using**?
31 | * **Use a clear and descriptive title** for the issue to identify the problem.
32 | * **Describe the exact steps which reproduce the problem** in as many details as possible. Don't just say what you did, but explain how you did it. For example, if you moved the cursor to the end of a line, explain if you used the mouse, or a touch swipe.
33 | * **Explain which behavior you expected to see instead and why.**
34 | * ~~**If the problem is related to performance**, include a [CPU profile capture and a screenshot](https://atom.io/docs/latest/hacking-atom-debugging#diagnose-performance-problems-with-the-dev-tools-cpu-profiler) with your report.~~ [work-in-progress]
35 |
36 | ##### **When reporting a browser issues:**
37 | * **Include the output of your JavaScript console**:
38 | * See [How to open the Javascript Console in different browsers](http://webmasters.stackexchange.com/a/77337)
39 | * Use pastebin or [gist](https://gist.github.com/) to store your javascript log
40 |
41 | ##### **When reporting a layout issues:**
42 | * **Include screenshots and animated GIFs**. You can use [this tool](http://www.cockos.com/licecap/) to record GIFs on OSX and Windows, and [this tool](https://github.com/colinkeenan/silentcast) or [this tool](https://github.com/GNOME/byzanz) on Linux.
43 |
44 | ##### **When reporting OS/Touchscreen issues:**
45 | * **What's the name and version of the OS you're using**?
46 | Include details about your configuration and environment:
47 |
48 |
49 | #### Template For Submitting Bug Reports
50 | ```
51 | [Short description of problem here]
52 |
53 | **Reproduction Steps:**
54 | 1. [First Step]
55 | 2. [Second Step]
56 | 3. [Other Steps...]
57 |
58 | **Expected behavior:**
59 | [Describe expected behavior here]
60 |
61 | **Observed behavior:**
62 | [Describe observed behavior here]
63 |
64 | **Screenshots and GIFs**
65 | 
66 |
67 | **Javascript console**
68 | 
69 |
70 | **Browser version:** [Enter Browser version here]
71 | **OctoPrint version:** [Enter OctoPrint version here]
72 | **TouchUI version:** [Enter TouchUI version here]
73 |
74 | **Installed plugins:**
75 | 1. [package 1]
76 | 2. [package 2]
77 | ```
78 |
79 | ### Your First Code Contribution
80 |
81 | Unsure where to begin contributing to TouchUI? You can start by looking through `help-wanted` issues:
82 |
83 | * [Help wanted issues][help-wanted] - issues which should be a bit more involved than `beginner` issues.
84 |
85 | ### Pull Requests
86 |
87 | * Include screenshots and animated GIFs in your pull request whenever possible.
88 | * ~~Follow the [JavaScript](https://github.com/styleguide/javascript),
89 | and [LESS](https://github.com/styleguide/css) styleguides.~~ [work-in-progress]
90 | * End files with a newline.
91 | * Target the branch maintenance
92 |
93 | ## Styleguides
94 |
95 | ### Git Commit Messages
96 |
97 | * Use the present tense ("Add feature" not "Added feature")
98 | * Use the imperative mood ("Move cursor to..." not "Moves cursor to...")
99 | * Limit the first line to 72 characters or less
100 | * Reference issues and pull requests liberally
101 | * Consider starting the commit message with an applicable emoji:
102 | * :sparkles: `:sparkles:` when adding a new feature
103 | * :bug: `:bug:` when fixing a bug
104 | * :fire: `:fire:` when removing code or files
105 | * :art: `:art:` when improving the format/structure of the code
106 | * :racehorse: `:racehorse:` when improving performance
107 | * :non-potable_water: `:non-potable_water:` when plugging memory leaks
108 | * :memo: `:memo:` when writing docs
109 | * :white_check_mark: `:white_check_mark:` when adding tests
110 | * :lock: `:lock:` when dealing with security
111 | * :arrow_up: `:arrow_up:` when upgrading dependencies
112 | * :arrow_down: `:arrow_down:` when downgrading dependencies
113 | * :shirt: `:shirt:` when removing linter warnings
114 |
115 | ---------
116 |
117 | Thanks to Atom for proving a solid contributing template.
118 |
--------------------------------------------------------------------------------
/DEVELOPMENT.md:
--------------------------------------------------------------------------------
1 | # Development envoirment
2 | * We manage javascript libraries with **Bower**
3 | 1. Libraries are downloaded and extrated into ``Source/vendors/``
4 | * We compile all files in the `Source` directory into multiple files with **Gulp**:
5 | 1. LESS compiles into a combined LESS file for the template of Customization.
6 | 1. LESS compiles into a CSS file used if Customization is disabled.
7 | 1. JS files is combined into one file:
8 | - Core files is written to touchui.bundled.js
9 | - Libraries is written to touchui.libraries.js
10 | - Knockout is written to touchui.libraries.js
11 |
12 |
13 | ## Prerequisites
14 | Install [NodeJS](http://www.nodejs.org/)
15 |
16 | 1. Install required global NPM plguins:
17 | ```
18 | (sudo) npm install gulp-cli -g
19 | ```
20 |
21 | 1. Install development dependencies
22 | ```
23 | npm install
24 | ```
25 |
26 | ## Commands
27 | - **Build all**
28 | Run `gulp`
29 |
30 | - **Watch**
31 | Run `gulp watch`
32 |
33 | - **LESS**
34 | Run `gulp less`
35 | This will concat all files in `source/less` to `touchui.bundled.less`
36 |
37 | - **JS**
38 | Run `gulp js`
39 | This will concat all files in `source/js` to `touchui.bundled.js`/`touchui.libraries.js`/`touchui.knockout.js`
40 |
--------------------------------------------------------------------------------
/MANIFEST.in:
--------------------------------------------------------------------------------
1 | include README.md
2 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # OctoPrint-TouchUI
2 | This plugin will transform the OctoPrint layout into a Mobile/TFT friendly layout. With larger buttons and a responsive layout down to the smallest resolution possible. It will mimic pointer events as touch, so you can hook up those touchscreens. It also supports a virtual keyboard.
3 |
4 | All these settings are set client-side, so we won't interfere with other clients. All settings are stored in your localstorage or as a delicious cookie. You can find the `TouchUI settings` in a dedicated modal. Remember they're stored on your device, so if you login with your desktop computer you won't get the touch interface.
5 |
6 | 
7 |
8 | ## Setup
9 | Install via the bundled [Plugin Manager](https://github.com/foosel/OctoPrint/wiki/Plugin:-Plugin-Manager)
10 | or manually using this URL:
11 |
12 | https://github.com/BillyBlaze/OctoPrint-TouchUI/archive/master.zip
13 |
14 | - **Touchscreens**
15 | Read more about [setting up a touchscreen](https://github.com/BillyBlaze/OctoPrint-TouchUI/wiki/Setup#raspberrypi--touchscreen) on our Wiki.
16 |
17 | ## Configuration
18 | The interface will automatically start when your browser is smaller then 980 pixels in width or if you're browsing with a touch device. You can turn this manually on and off in the ``TouchUI settings`` modal. Alternatively you can force TouchUI to load by adding ``#touch`` on the end of your URL.
19 |
20 | Read more [configuration options](https://github.com/BillyBlaze/OctoPrint-TouchUI/wiki/Configuration) on our Wiki.
21 |
22 | - **Customization**
23 | You can change 4 main colors of the interface with the power of LESS. If you would like to change more colors, then you're free to add your own LESS file. [Read more about this and the variables](https://github.com/BillyBlaze/OctoPrint-TouchUI/wiki/Customize:-Use-your-own-file) on our wiki.
24 |
25 | ## Supported browsers
26 | 1. Chrome 30+
27 | 1. Firefox 40+
28 | 1. Safari Mobile
29 | 1. Chrome Mobile
30 |
--------------------------------------------------------------------------------
/WHATSNEW.md:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BillyBlaze/OctoPrint-TouchUI/73665b2d6fd9d735f383339620a049771dfe0f6f/WHATSNEW.md
--------------------------------------------------------------------------------
/babel.cfg:
--------------------------------------------------------------------------------
1 | [python: */**.py]
2 | [jinja2: */**.jinja2]
3 | extensions=jinja2.ext.autoescape, jinja2.ext.with_
4 |
5 | [javascript: */**.js]
6 | extract_messages = gettext, ngettext
7 |
--------------------------------------------------------------------------------
/gulpfile.js:
--------------------------------------------------------------------------------
1 | var fs = require('fs');
2 | var gulp = require('gulp');
3 | var less = require('gulp-less');
4 | var del = require('del');
5 | var rename = require("gulp-rename");
6 | var uglify = require("gulp-uglify");
7 | var through = require("through2");
8 | var gulpif = require("gulp-if");
9 | var trimlines = require('gulp-trimlines');
10 | var concat = require('gulp-concat');
11 | var stripCssComments = require('gulp-strip-css-comments');
12 |
13 | gulp.task('lessc', function () {
14 | return gulp.src('source/less/touchui.less')
15 | .pipe(less({compress: true}))
16 | .pipe(through.obj(function(file, enc, cb) {
17 | var contents = file.contents.toString();
18 | contents = contents.replace(/mixin\:placeholder\;/g, '');
19 |
20 | file.contents = Buffer.from(contents);
21 |
22 | cb(null, file);
23 | }))
24 | .pipe(gulp.dest('octoprint_touchui/static/css'));
25 | });
26 |
27 | gulp.task("less:concat", function() {
28 | return gulp.src('source/less/touchui.less')
29 | .pipe(through.obj(function(file, enc, cb) {
30 | var contents = file.contents.toString();
31 | var regex = /@import \"(.*)\"\;/gm;
32 |
33 | while ((m = regex.exec(contents)) !== null) {
34 | if (m.index === regex.lastIndex) {
35 | regex.lastIndex++;
36 | }
37 |
38 | var replaceString = m[0];
39 | var fileName = m[1];
40 |
41 | var importContents = fs.readFileSync(__dirname + '/source/less/' + fileName);
42 |
43 | contents = contents.replace(replaceString, importContents);
44 | }
45 |
46 | file.contents = Buffer.from(contents);
47 |
48 | cb(null, file);
49 | }))
50 | .pipe(stripCssComments())
51 | .pipe(rename("touchui.bundled.less"))
52 | .pipe(trimlines())
53 | .pipe(through.obj(function(file, enc, cb) {
54 | var contents = file.contents.toString();
55 | contents = contents.replace(/\n\s*\n/g, '\n');
56 |
57 | file.contents = Buffer.from(contents);
58 |
59 | cb(null, file);
60 | }))
61 | .pipe(gulp.dest('octoprint_touchui/static/less/'));
62 | });
63 |
64 | gulp.task('clean:hash', function () {
65 | return del([
66 | 'octoprint_touchui/static/css/hash.*.touchui',
67 | ]);
68 | });
69 |
70 | gulp.task('js:concat:libs', function () {
71 | return gulp.src([
72 | 'node_modules/keyboard/dist/js/jquery.keyboard.min.js',
73 | 'node_modules/jquery-fullscreen-kayahr/jquery.fullscreen-min.js',
74 | 'node_modules/iscroll/build/iscroll.js',
75 | 'source/vendors/tinycolorpicker/lib/jquery.tinycolorpicker.min.js'
76 | ])
77 | .pipe(gulpif("**/iscroll.js", uglify()))
78 | .pipe(concat('touchui.libraries.js'))
79 | .pipe(gulp.dest('octoprint_touchui/static/js/'));
80 | });
81 |
82 | gulp.task('js:concat:app', function () {
83 | return gulp.src([
84 | 'source/js/constructor.js',
85 | 'source/js/**/*.js',
86 | 'source/js/**/**/*.js',
87 | '!source/js/bootstrap.js'
88 | ])
89 | .pipe(concat('touchui.bundled.js'))
90 | //.pipe(uglify())
91 | .pipe(gulp.dest('octoprint_touchui/static/js/'));
92 | });
93 |
94 | gulp.task('js:concat:bootstrap', function () {
95 | return gulp.src([
96 | 'source/js/bootstrap.js'
97 | ])
98 | .pipe(rename("touchui.bootstrap.js"))
99 | .pipe(uglify())
100 | .pipe(gulp.dest('octoprint_touchui/static/js/'));
101 | });
102 |
103 | gulp.task('watch', function () {
104 | gulp.watch(
105 | ['source/js/**/*.js', 'source/js/*.js'],
106 | gulp.series('js')
107 | );
108 | gulp.watch(
109 | ['source/less/touchui.less', 'source/less/**/*.less'],
110 | gulp.series('less')
111 | );
112 | });
113 |
114 | gulp.task('default', gulp.series('lessc', 'clean:hash', 'less:concat', 'js:concat:app', 'js:concat:libs', 'js:concat:bootstrap'));
115 | gulp.task('less', gulp.series('lessc', 'clean:hash', 'less:concat'));
116 | gulp.task('js', gulp.series('js:concat:app', 'js:concat:libs', 'js:concat:bootstrap'));
117 |
--------------------------------------------------------------------------------
/octoprint_touchui/__init__.py:
--------------------------------------------------------------------------------
1 | # coding=utf-8
2 | from __future__ import absolute_import
3 |
4 | from .api import touchui_api
5 | from .customization import touchui_customization
6 |
7 | import octoprint.plugin
8 | import octoprint.settings
9 | import octoprint.util
10 | import hashlib
11 | import time
12 | import os
13 |
14 | class touchui_core( touchui_api,
15 | touchui_customization,
16 | octoprint.plugin.SettingsPlugin,
17 | octoprint.plugin.AssetPlugin,
18 | octoprint.plugin.TemplatePlugin,
19 | octoprint.plugin.StartupPlugin):
20 |
21 | def __init__(self):
22 | super(touchui_core, self).__init__()
23 | self._whatsNewPath = os.path.dirname(__file__) + "/WHATSNEW.md"
24 |
25 | def on_startup(self, host, port):
26 | self._startup_customization(host, port)
27 |
28 | def on_settings_load(self):
29 | return self._load_custom_settings()
30 |
31 | def on_settings_save(self, data):
32 | self._save_custom_settings(data)
33 |
34 | def on_after_startup(self):
35 | self._check_customization()
36 |
37 | def get_template_vars(self):
38 | if os.path.isfile(self._customCssPath) and self._settings.get(["useCustomization"]):
39 | with open(self._customCssPath, 'r') as contentFile:
40 | return dict(cssPath="./plugin/touchui/static/css/touchui.custom.{port}.css".format(port=self._port), timestamp=hashlib.md5(contentFile.read().encode('utf-8')).hexdigest()[:9])
41 | else:
42 | with open(self._cssPath, 'r') as contentFile:
43 | return dict(cssPath="./plugin/touchui/static/css/touchui.css", timestamp=hashlib.md5(contentFile.read().encode('utf-8')).hexdigest()[:9])
44 |
45 | def get_assets(self):
46 | return dict(
47 | js=["js/touchui.libraries.js", "js/touchui.bundled.js", "js/touchui.bootstrap.js"]
48 | )
49 |
50 | def get_template_configs(self):
51 | files = [
52 | dict(type="generic", template="touchui_modal.jinja2", custom_bindings=True),
53 | dict(type="settings", template="touchui_settings.jinja2", custom_bindings=True),
54 | dict(type="navbar", template="touchui_menu_item.jinja2", custom_bindings=True),
55 | dict(type="generic", template="touchui_load_css.jinja2", custom_bindings=False)
56 | ]
57 |
58 | if self._settings.get(["automaticallyLoadResolution"]):
59 | files.append(
60 | dict(type="navbar", template="empty.jinja2", custom_bindings=False, div="touchui_auto_load_resolution")
61 | )
62 |
63 | if self._settings.get(["automaticallyLoadTouch"]):
64 | files.append(
65 | dict(type="navbar", template="empty.jinja2", custom_bindings=False, div="touchui_auto_load_touch")
66 | )
67 |
68 | return files
69 |
70 | def get_settings_defaults(self):
71 | return dict(
72 | hasVisibleSettings=True,
73 | automaticallyLoadResolution=True,
74 | automaticallyLoadTouch=True,
75 | useCustomization=False,
76 | closeDialogsOutside=False,
77 | colors=dict(
78 | mainColor="#00B0FF",
79 | termColor="#0F0",
80 | bgColor="#000",
81 | textColor="#FFF",
82 | fontSize="16",
83 | customPath="",
84 | useLocalFile=False
85 | )
86 | )
87 |
88 | def get_version(self):
89 | return self._plugin_version
90 |
91 | def get_update_information(self):
92 | return dict(
93 | touchui=dict(
94 | displayName="TouchUI",
95 | displayVersion=self._plugin_version,
96 | type="github_release",
97 | user="BillyBlaze",
98 | repo="OctoPrint-TouchUI",
99 | current=self._plugin_version,
100 | pip="https://github.com/BillyBlaze/OctoPrint-TouchUI/archive/{target_version}.zip"
101 | )
102 | )
103 |
104 | __plugin_name__ = "TouchUI"
105 | __plugin_pythoncompat__ = ">=2.7,<4"
106 | def __plugin_load__():
107 | touchui = touchui_core()
108 |
109 | global __plugin_implementation__
110 | __plugin_implementation__ = touchui
111 |
112 | global __plugin_hooks__
113 | __plugin_hooks__ = {
114 | "octoprint.plugin.softwareupdate.check_config": __plugin_implementation__.get_update_information,
115 | "octoprint.server.http.bodysize": __plugin_implementation__.increase_upload_bodysize
116 | }
117 |
--------------------------------------------------------------------------------
/octoprint_touchui/api.py:
--------------------------------------------------------------------------------
1 | # coding=utf-8
2 | from __future__ import absolute_import
3 |
4 | from .decorators import crossdomain, touchui_admin_permission
5 | from octoprint.server.util.flask import restricted_access
6 |
7 | import octoprint.plugin
8 | import octoprint.settings
9 | import octoprint.util
10 | import flask
11 | import functools
12 | import os
13 |
14 | class touchui_api(octoprint.plugin.BlueprintPlugin):
15 | def increase_upload_bodysize(self, current_max_body_sizes, *args, **kwargs):
16 | return [("POST", r"/css", 1 * 1024 * 1024), ("GET", r"/css", 1 * 1024 * 1024)]
17 |
18 | @octoprint.plugin.BlueprintPlugin.route("/css", methods=["POST"])
19 | @restricted_access
20 | @touchui_admin_permission
21 | def saveCSS(self):
22 | if not "css" in flask.request.values:
23 | return flask.make_response("Expected a CSS value.", 400)
24 |
25 | try:
26 | self._save_custom_css(flask.request.values["css"])
27 |
28 | except Exception as e:
29 | self._logger.warn("Exception while generating LESS file: {message}".format(message=str(e)))
30 | return flask.make_response(str(e), 400)
31 |
32 | return flask.make_response("Ok.", 200)
33 |
34 | @octoprint.plugin.BlueprintPlugin.route("/css", methods=["GET"])
35 | @restricted_access
36 | @touchui_admin_permission
37 | def getCSS(self):
38 | data = ""
39 |
40 | if not "path" in flask.request.values:
41 | return flask.make_response("Expected a path value.", 400)
42 |
43 | try:
44 | with open(flask.request.values["path"], 'r') as contentFile:
45 | data = contentFile.read()
46 |
47 | except Exception as e:
48 | self._logger.warn("Exception while generating LESS file: {message}".format(message=str(e)))
49 | return flask.make_response(str(e), 400)
50 |
51 | return flask.make_response(data, 200)
52 |
53 | @octoprint.plugin.BlueprintPlugin.route("/ping", methods=["GET"])
54 | @crossdomain(origin='*')
55 | def ping(self):
56 | return flask.make_response("Ok.", 200)
57 |
58 | def is_blueprint_protected(self):
59 | return False
60 |
--------------------------------------------------------------------------------
/octoprint_touchui/customization.py:
--------------------------------------------------------------------------------
1 | # coding=utf-8
2 | from __future__ import absolute_import
3 |
4 | try:
5 | from octoprint.access.permissions import Permissions
6 | except:
7 | from octoprint.server import admin_permission
8 |
9 | import octoprint.plugin
10 | import octoprint.settings
11 | import octoprint.util
12 | import hashlib
13 | import time
14 | import os
15 |
16 | class touchui_customization(object):
17 | def _startup_customization(self, host, port):
18 | self._port = port
19 | self._cssPath = "{base}/static/css/touchui.css".format(base=os.path.dirname(__file__))
20 | self._customCssPath = "{base}/static/css/touchui.custom.{port}.css".format(base=os.path.dirname(__file__), port=port)
21 | self._customLessPath = "{base}/static/less/touchui.bundled.less".format(base=os.path.dirname(__file__))
22 | self._customHashPath = "{base}/static/css/hash.{port}.touchui".format(base=os.path.dirname(__file__), port=port)
23 | self._requireNewCSS = False
24 | self._refreshCSS = False
25 | self._refreshTime = 0
26 |
27 | # Migrate old css file to path with port
28 | if os.path.isfile("{base}/static/css/touchui.custom.css".format(base=os.path.dirname(__file__))):
29 | os.rename(
30 | "{base}/static/css/touchui.custom.css".format(base=os.path.dirname(__file__)),
31 | "{base}/static/css/touchui.custom.{port}.css".format(base=os.path.dirname(__file__), port=port)
32 | )
33 |
34 | # Migrate old hash file to path with port
35 | if os.path.isfile("{base}/static/css/hash.touchui".format(base=os.path.dirname(__file__))):
36 | os.rename(
37 | "{base}/static/css/hash.touchui".format(base=os.path.dirname(__file__)),
38 | "{base}/static/css/hash.{port}.touchui".format(base=os.path.dirname(__file__), port=port)
39 | )
40 |
41 | def _check_customization(self):
42 | # When generating LESS to CSS we also store the LESS contents into a md5 hash
43 | # if the hash of the local LESS file doesn't match this saved hash, then that indicates
44 | # that the LESS file has been update and that it requires a new compile.
45 | #
46 | # Therefor we going to put _requireNewCSS on TRUE, and we then let an admin compile the
47 | # LESS to CSS and store it in local CSS file.
48 | if self._settings.get(["useCustomization"]):
49 | hashedNew = "1"
50 | hashedOld = "2"
51 |
52 | if os.path.isfile(self._customLessPath):
53 | with open(self._customLessPath, 'r') as contentFile:
54 | hashedNew = hashlib.md5(contentFile.read().encode('utf-8')).hexdigest()
55 |
56 | if os.path.isfile(self._customHashPath):
57 | with open(self._customHashPath, 'r') as contentFile:
58 | hashedOld = contentFile.read()
59 |
60 | if hashedNew != hashedOld:
61 | self._requireNewCSS = True
62 |
63 | else:
64 | self._remove_custom_css()
65 |
66 | def _load_custom_settings(self):
67 | data = dict(octoprint.plugin.SettingsPlugin.on_settings_load(self))
68 | data["hasCustom"] = os.path.isfile(self._customCssPath)
69 | data["requireNewCSS"] = self._requireNewCSS
70 | data["refreshCSS"] = self._refreshCSS
71 | data["whatsNew"] = False
72 |
73 | try:
74 | plugin_permission = Permissions.SETTINGS.can()
75 | except:
76 | plugin_permission = admin_permission.can()
77 |
78 | if plugin_permission:
79 | if os.path.isfile(self._whatsNewPath):
80 | with open(self._whatsNewPath, 'r') as contentFile:
81 | data["whatsNew"] = contentFile.read()
82 | os.unlink(self._whatsNewPath)
83 |
84 | if self._requireNewCSS is True:
85 | self._requireNewCSS = False
86 |
87 | if self._settings.get(["useCustomization"]):
88 | if os.path.isfile(self._customHashPath) is not True:
89 | data["requireNewCSS"] = True
90 |
91 | if self._refreshCSS:
92 | if self._refreshTime < time.time():
93 | data["refreshCSS"] = False
94 | self._refreshCSS = False
95 | self._refreshTime = 0
96 |
97 | return data
98 |
99 | def _save_custom_settings(self, data):
100 | octoprint.plugin.SettingsPlugin.on_settings_save(self, data)
101 |
102 | if self._settings.get(["useCustomization"]) is False:
103 | self._remove_custom_css()
104 | else:
105 | self._refreshCSS = True
106 | self._refreshTime = time.time() + 10
107 |
108 | def _save_custom_css(self, data):
109 | self._requireNewCSS = False
110 | hashed = ""
111 |
112 | with open(self._customCssPath, "w+") as customCSS:
113 | customCSS.write('{css}'.format(css=data))
114 |
115 | if os.path.isfile(self._customLessPath):
116 | with open(self._customLessPath, 'r') as contentFile:
117 | hashed = hashlib.md5(contentFile.read().encode('utf-8')).hexdigest()
118 |
119 | with open(self._customHashPath, "w+") as customHash:
120 | customHash.write('{hash}'.format(hash=hashed))
121 |
122 | def _remove_custom_css(self):
123 | self._requireNewCSS = False
124 |
125 | if os.path.isfile(self._customCssPath):
126 | os.unlink(self._customCssPath)
127 |
128 | if os.path.isfile(self._customHashPath):
129 | os.unlink(self._customHashPath)
130 |
--------------------------------------------------------------------------------
/octoprint_touchui/decorators.py:
--------------------------------------------------------------------------------
1 | # coding=utf-8
2 | from __future__ import absolute_import
3 |
4 | from datetime import timedelta
5 | from flask import make_response, request, current_app, abort
6 | from functools import update_wrapper, wraps
7 |
8 | try:
9 | from octoprint.access.permissions import Permissions
10 | except:
11 | from octoprint.server import admin_permission
12 |
13 | def touchui_admin_permission(f):
14 | @wraps(f)
15 | def decorated_function(*args, **kwargs):
16 | try:
17 | plugin_permission = Permissions.SETTINGS.can()
18 | except:
19 | plugin_permission = admin_permission.can()
20 |
21 | if not plugin_permission:
22 | return abort(403)
23 |
24 | return f(*args, **kwargs)
25 | return decorated_function
26 |
27 | def crossdomain(origin=None, methods=None, headers=None,
28 | max_age=21600, attach_to_all=True,
29 | automatic_options=True):
30 |
31 | # Py3 compatibility with Py2
32 | try:
33 | basestring
34 | except NameError:
35 | basestring = str
36 |
37 | if methods is not None:
38 | methods = ', '.join(sorted(x.upper() for x in methods))
39 | if headers is not None and not isinstance(headers, basestring):
40 | headers = ', '.join(x.upper() for x in headers)
41 | if not isinstance(origin, basestring):
42 | origin = ', '.join(origin)
43 | if isinstance(max_age, timedelta):
44 | max_age = max_age.total_seconds()
45 |
46 | def get_methods():
47 | if methods is not None:
48 | return methods
49 |
50 | options_resp = current_app.make_default_options_response()
51 | return options_resp.headers['allow']
52 |
53 | def decorator(f):
54 | def wrapped_function(*args, **kwargs):
55 | if automatic_options and request.method == 'OPTIONS':
56 | resp = current_app.make_default_options_response()
57 | else:
58 | resp = make_response(f(*args, **kwargs))
59 | if not attach_to_all and request.method != 'OPTIONS':
60 | return resp
61 |
62 | h = resp.headers
63 |
64 | h['Access-Control-Allow-Origin'] = origin
65 | h['Access-Control-Allow-Methods'] = get_methods()
66 | h['Access-Control-Max-Age'] = str(max_age)
67 | if headers is not None:
68 | h['Access-Control-Allow-Headers'] = headers
69 | return resp
70 |
71 | f.provide_automatic_options = False
72 | return update_wrapper(wrapped_function, f)
73 | return decorator
74 |
--------------------------------------------------------------------------------
/octoprint_touchui/static/README.md:
--------------------------------------------------------------------------------
1 | # All JS/CSS and LESS files are generated!
2 |
3 | Read the [DEVELOPMENT.md](https://github.com/BillyBlaze/OctoPrint-TouchUI/blob/master/DEVELOPMENT.md) for more details about chaning codes and re-generating it.
4 |
--------------------------------------------------------------------------------
/octoprint_touchui/static/css/.gitignore:
--------------------------------------------------------------------------------
1 | touchui.custom.*.css
2 | hash.*.touchui
3 |
--------------------------------------------------------------------------------
/octoprint_touchui/static/fonts/fontawesome/FontAwesome.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BillyBlaze/OctoPrint-TouchUI/73665b2d6fd9d735f383339620a049771dfe0f6f/octoprint_touchui/static/fonts/fontawesome/FontAwesome.otf
--------------------------------------------------------------------------------
/octoprint_touchui/static/fonts/fontawesome/fontawesome-webfont.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BillyBlaze/OctoPrint-TouchUI/73665b2d6fd9d735f383339620a049771dfe0f6f/octoprint_touchui/static/fonts/fontawesome/fontawesome-webfont.eot
--------------------------------------------------------------------------------
/octoprint_touchui/static/fonts/fontawesome/fontawesome-webfont.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BillyBlaze/OctoPrint-TouchUI/73665b2d6fd9d735f383339620a049771dfe0f6f/octoprint_touchui/static/fonts/fontawesome/fontawesome-webfont.ttf
--------------------------------------------------------------------------------
/octoprint_touchui/static/fonts/fontawesome/fontawesome-webfont.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BillyBlaze/OctoPrint-TouchUI/73665b2d6fd9d735f383339620a049771dfe0f6f/octoprint_touchui/static/fonts/fontawesome/fontawesome-webfont.woff
--------------------------------------------------------------------------------
/octoprint_touchui/static/fonts/fontawesome/fontawesome-webfont.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BillyBlaze/OctoPrint-TouchUI/73665b2d6fd9d735f383339620a049771dfe0f6f/octoprint_touchui/static/fonts/fontawesome/fontawesome-webfont.woff2
--------------------------------------------------------------------------------
/octoprint_touchui/static/fonts/sourcecodepro/LICENSE.txt:
--------------------------------------------------------------------------------
1 | Copyright 2010, 2012 Adobe Systems Incorporated (http://www.adobe.com/), with Reserved Font Name 'Source'. All Rights Reserved. Source is a trademark of Adobe Systems Incorporated in the United States and/or other countries.
2 |
3 | This Font Software is licensed under the SIL Open Font License, Version 1.1.
4 |
5 | This license is copied below, and is also available with a FAQ at: http://scripts.sil.org/OFL
6 |
7 |
8 | -----------------------------------------------------------
9 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
10 | -----------------------------------------------------------
11 |
12 | PREAMBLE
13 | The goals of the Open Font License (OFL) are to stimulate worldwide
14 | development of collaborative font projects, to support the font creation
15 | efforts of academic and linguistic communities, and to provide a free and
16 | open framework in which fonts may be shared and improved in partnership
17 | with others.
18 |
19 | The OFL allows the licensed fonts to be used, studied, modified and
20 | redistributed freely as long as they are not sold by themselves. The
21 | fonts, including any derivative works, can be bundled, embedded,
22 | redistributed and/or sold with any software provided that any reserved
23 | names are not used by derivative works. The fonts and derivatives,
24 | however, cannot be released under any other type of license. The
25 | requirement for fonts to remain under this license does not apply
26 | to any document created using the fonts or their derivatives.
27 |
28 | DEFINITIONS
29 | "Font Software" refers to the set of files released by the Copyright
30 | Holder(s) under this license and clearly marked as such. This may
31 | include source files, build scripts and documentation.
32 |
33 | "Reserved Font Name" refers to any names specified as such after the
34 | copyright statement(s).
35 |
36 | "Original Version" refers to the collection of Font Software components as
37 | distributed by the Copyright Holder(s).
38 |
39 | "Modified Version" refers to any derivative made by adding to, deleting,
40 | or substituting -- in part or in whole -- any of the components of the
41 | Original Version, by changing formats or by porting the Font Software to a
42 | new environment.
43 |
44 | "Author" refers to any designer, engineer, programmer, technical
45 | writer or other person who contributed to the Font Software.
46 |
47 | PERMISSION & CONDITIONS
48 | Permission is hereby granted, free of charge, to any person obtaining
49 | a copy of the Font Software, to use, study, copy, merge, embed, modify,
50 | redistribute, and sell modified and unmodified copies of the Font
51 | Software, subject to the following conditions:
52 |
53 | 1) Neither the Font Software nor any of its individual components,
54 | in Original or Modified Versions, may be sold by itself.
55 |
56 | 2) Original or Modified Versions of the Font Software may be bundled,
57 | redistributed and/or sold with any software, provided that each copy
58 | contains the above copyright notice and this license. These can be
59 | included either as stand-alone text files, human-readable headers or
60 | in the appropriate machine-readable metadata fields within text or
61 | binary files as long as those fields can be easily viewed by the user.
62 |
63 | 3) No Modified Version of the Font Software may use the Reserved Font
64 | Name(s) unless explicit written permission is granted by the corresponding
65 | Copyright Holder. This restriction only applies to the primary font name as
66 | presented to the users.
67 |
68 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
69 | Software shall not be used to promote, endorse or advertise any
70 | Modified Version, except to acknowledge the contribution(s) of the
71 | Copyright Holder(s) and the Author(s) or with their explicit written
72 | permission.
73 |
74 | 5) The Font Software, modified or unmodified, in part or in whole,
75 | must be distributed entirely under this license, and must not be
76 | distributed under any other license. The requirement for fonts to
77 | remain under this license does not apply to any document created
78 | using the Font Software.
79 |
80 | TERMINATION
81 | This license becomes null and void if any of the above conditions are
82 | not met.
83 |
84 | DISCLAIMER
85 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
86 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
87 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
88 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
89 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
90 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
91 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
92 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
93 | OTHER DEALINGS IN THE FONT SOFTWARE.
94 |
--------------------------------------------------------------------------------
/octoprint_touchui/static/fonts/sourcecodepro/SourceCodePro-Bold.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BillyBlaze/OctoPrint-TouchUI/73665b2d6fd9d735f383339620a049771dfe0f6f/octoprint_touchui/static/fonts/sourcecodepro/SourceCodePro-Bold.woff
--------------------------------------------------------------------------------
/octoprint_touchui/static/fonts/sourcecodepro/SourceCodePro-Light.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BillyBlaze/OctoPrint-TouchUI/73665b2d6fd9d735f383339620a049771dfe0f6f/octoprint_touchui/static/fonts/sourcecodepro/SourceCodePro-Light.woff
--------------------------------------------------------------------------------
/octoprint_touchui/static/fonts/sourcecodepro/SourceCodePro-Regular.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BillyBlaze/OctoPrint-TouchUI/73665b2d6fd9d735f383339620a049771dfe0f6f/octoprint_touchui/static/fonts/sourcecodepro/SourceCodePro-Regular.woff
--------------------------------------------------------------------------------
/octoprint_touchui/static/fonts/sourcecodepro/SourceCodePro-Semibold.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BillyBlaze/OctoPrint-TouchUI/73665b2d6fd9d735f383339620a049771dfe0f6f/octoprint_touchui/static/fonts/sourcecodepro/SourceCodePro-Semibold.woff
--------------------------------------------------------------------------------
/octoprint_touchui/static/fonts/touchui/touchui.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BillyBlaze/OctoPrint-TouchUI/73665b2d6fd9d735f383339620a049771dfe0f6f/octoprint_touchui/static/fonts/touchui/touchui.eot
--------------------------------------------------------------------------------
/octoprint_touchui/static/fonts/touchui/touchui.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Generated by IcoMoon
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/octoprint_touchui/static/fonts/touchui/touchui.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BillyBlaze/OctoPrint-TouchUI/73665b2d6fd9d735f383339620a049771dfe0f6f/octoprint_touchui/static/fonts/touchui/touchui.ttf
--------------------------------------------------------------------------------
/octoprint_touchui/static/fonts/touchui/touchui.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BillyBlaze/OctoPrint-TouchUI/73665b2d6fd9d735f383339620a049771dfe0f6f/octoprint_touchui/static/fonts/touchui/touchui.woff
--------------------------------------------------------------------------------
/octoprint_touchui/static/img/colorpicker.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BillyBlaze/OctoPrint-TouchUI/73665b2d6fd9d735f383339620a049771dfe0f6f/octoprint_touchui/static/img/colorpicker.png
--------------------------------------------------------------------------------
/octoprint_touchui/static/js/touchui.bootstrap.js:
--------------------------------------------------------------------------------
1 | !function(){if(TouchUI.prototype.settings.hasBootloader&&window.log&&window.log.error){var r=window.log.error;window.log.error=function(o,n){window.top.postMessage([n,""],"*"),r.apply(window.log,arguments)}}var o=new TouchUI;o.domLoading(),$(function(){o.domReady(),OCTOPRINT_VIEWMODELS.push([o.koStartup,o.TOUCHUI_REQUIRED_VIEWMODELS,o.TOUCHUI_ELEMENTS,o.TOUCHUI_REQUIRED_VIEWMODELS])})}();
--------------------------------------------------------------------------------
/octoprint_touchui/templates/empty.jinja2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BillyBlaze/OctoPrint-TouchUI/73665b2d6fd9d735f383339620a049771dfe0f6f/octoprint_touchui/templates/empty.jinja2
--------------------------------------------------------------------------------
/octoprint_touchui/templates/touchui_load_css.jinja2:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/octoprint_touchui/templates/touchui_menu_item.jinja2:
--------------------------------------------------------------------------------
1 |
2 | {{ _('TouchUI settings') }}
3 |
4 |
--------------------------------------------------------------------------------
/octoprint_touchui/templates/touchui_modal.jinja2:
--------------------------------------------------------------------------------
1 |
38 |
--------------------------------------------------------------------------------
/octoprint_touchui/templates/touchui_settings.jinja2:
--------------------------------------------------------------------------------
1 |
2 |
3 |
116 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "author": "",
3 | "name": "OctoPrint-TouchUI",
4 | "version": "0.0.0",
5 | "repository": {
6 | "type": "git",
7 | "url": "git://github.com/BillyBlaze/OctoPrint-TouchUI.git"
8 | },
9 | "dependencies": {
10 | "virtual-keyboard": "https://github.com/Mottie/Keyboard/archive/v1.27.2.tar.gz"
11 | },
12 | "devDependencies": {
13 | "del": "^5.1.0",
14 | "gulp": "^4.0.2",
15 | "gulp-concat": "^2.6.1",
16 | "gulp-if": "^3.0.0",
17 | "gulp-less": "^4.0.1",
18 | "gulp-rename": "^2.0.0",
19 | "gulp-strip-css-comments": "^2.0.0",
20 | "gulp-trimlines": "^1.0.1",
21 | "gulp-uglify": "^3.0.2",
22 | "iscroll": "~5.2.0",
23 | "jquery-fullscreen-kayahr": "~1.1.5",
24 | "keyboard": "https://github.com/Mottie/Keyboard/archive/v1.27.2.tar.gz",
25 | "through2": "^4.0.2"
26 | },
27 | "optionalDependencies": {},
28 | "engines": {
29 | "node": "*"
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | ###
2 | # This file is only here to make sure that something like
3 | #
4 | # pip install -e .
5 | #
6 | # works as expected. Requirements can be found in setup.py.
7 | ###
8 |
9 | .
10 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | # coding=utf-8
2 |
3 | plugin_identifier = "touchui"
4 | plugin_package = "octoprint_touchui"
5 | plugin_name = "TouchUI"
6 | plugin_version = "0.3.18"
7 | plugin_description = """A touch friendly interface for a small TFT modules and or phones"""
8 | plugin_author = "Paul de Vries"
9 | plugin_author_email = "pablo+octoprint+touch+ui@aerosol.me"
10 | plugin_url = "https://github.com/BillyBlaze/OctoPrint-TouchUI"
11 | plugin_license = "AGPLv3"
12 | plugin_requires = ["OctoPrint>=1.2.4"]
13 | plugin_additional_data = []
14 | plugin_addtional_packages = []
15 | plugin_ignored_packages = []
16 | additional_setup_parameters = {}
17 |
18 | from setuptools import setup
19 |
20 | try:
21 | import octoprint_setuptools
22 | except:
23 | print("Could not import OctoPrint's setuptools, are you sure you are running that under "
24 | "the same python installation that OctoPrint is installed under?")
25 | import sys
26 | sys.exit(-1)
27 |
28 | setup_parameters = octoprint_setuptools.create_plugin_setup_parameters(
29 | identifier=plugin_identifier,
30 | package=plugin_package,
31 | name=plugin_name,
32 | version=plugin_version,
33 | description=plugin_description,
34 | author=plugin_author,
35 | mail=plugin_author_email,
36 | url=plugin_url,
37 | license=plugin_license,
38 | requires=plugin_requires,
39 | additional_packages=plugin_addtional_packages,
40 | ignored_packages=plugin_ignored_packages,
41 | additional_data=plugin_additional_data
42 | )
43 |
44 | if len(additional_setup_parameters):
45 | from octoprint.util import dict_merge
46 | setup_parameters = dict_merge(setup_parameters, additional_setup_parameters)
47 |
48 | setup(**setup_parameters)
49 |
50 | try:
51 | import os
52 | fileOut = "./octoprint_touchui/WHATSNEW.md"
53 | fileIn = "./WHATSNEW.md"
54 |
55 | if os.path.isfile(fileOut):
56 | os.unlink(fileOut)
57 |
58 | with open(fileIn, 'r') as contentFile:
59 | whatsNew = contentFile.read()
60 |
61 | with open(fileOut, "w+") as writeFile:
62 | writeFile.write('{WHATSNEW}'.format(WHATSNEW=whatsNew))
63 |
64 | except Exception as e:
65 | print("\nCopying the WHATSNEW.md failed, however it shouldn't matter, just read the release notes on github if you like.\nThe error: {message}".format(message=str(e)))
66 |
--------------------------------------------------------------------------------
/source/js/animate/hide.js:
--------------------------------------------------------------------------------
1 | TouchUI.prototype.animate.hide = function(what) {
2 | var self = this;
3 |
4 | //Lets hide the navbar by scroll
5 | if( what === "navbar" ) {
6 | if( this.animate.isHidebarActive() ) {
7 | var navbar = $("#navbar"),
8 | navbarHeight = parseFloat(navbar.height());
9 |
10 | if( this.settings.hasTouch ) {
11 | // Hide navigation bar on mobile
12 | window.scrollTo(0,1);
13 |
14 | if(parseFloat($("html,body").prop('scrollHeight')) > ($(window).height() + navbarHeight)) {//hasEnoughScroll?
15 | $("html,body").stop().animate({
16 | scrollTop: navbarHeight
17 | }, 160, "swing");
18 | }
19 |
20 | } else {
21 | var scroll = self.scroll.iScrolls.body;
22 |
23 | if(scroll.isAnimating) {
24 | setTimeout(function() {
25 | self.animate.hide.call(self, what);
26 | }, 10);
27 | return;
28 | }
29 |
30 | setTimeout(function() {
31 | if(Math.abs(scroll.maxScrollY) > 0) {
32 | scroll.scrollTo(0, -navbarHeight, 160);
33 | }
34 | }, 0);
35 |
36 | }
37 | }
38 | }
39 |
40 | }
41 |
--------------------------------------------------------------------------------
/source/js/bootstrap.js:
--------------------------------------------------------------------------------
1 | !function() {
2 |
3 | // Catch errors
4 | if (TouchUI.prototype.settings.hasBootloader) {
5 | if (window.log && window.log.error) {
6 | var old = window.log.error;
7 | window.log.error = function(plugin, msg) {
8 | window.top.postMessage([msg, ''], "*");
9 | old.apply(window.log, arguments);
10 | }
11 | }
12 | }
13 |
14 | var Touch = new TouchUI();
15 | Touch.domLoading();
16 |
17 | $(function() {
18 | Touch.domReady();
19 |
20 | OCTOPRINT_VIEWMODELS.push([
21 | Touch.koStartup,
22 | Touch.TOUCHUI_REQUIRED_VIEWMODELS,
23 | Touch.TOUCHUI_ELEMENTS,
24 | Touch.TOUCHUI_REQUIRED_VIEWMODELS
25 | ]);
26 | });
27 |
28 | }();
29 |
--------------------------------------------------------------------------------
/source/js/components/dropdown.js:
--------------------------------------------------------------------------------
1 | TouchUI.prototype.components.dropdown = {
2 |
3 | init: function() {
4 | this.components.dropdown.toggleSubmenu.call( this );
5 | this.components.dropdown.toggle.call( this );
6 | },
7 |
8 | // Rewrite opening of dropdowns
9 | toggle: function() {
10 | var self = this;
11 | var namespace = ".touchui.dropdown";
12 |
13 | $(document)
14 | .off('.dropdown')
15 | .on('touchstart.dropdown.data-api', '.dropdown-menu', function (e) { e.stopPropagation() })
16 | .on('click.dropdown.data-api', '[data-toggle=dropdown]', function(e) {
17 | var $dropdownToggle = $(e.currentTarget);
18 | var $dropdownContainer = $dropdownToggle.parent();
19 |
20 | // Stop the hashtag from propagating
21 | e.preventDefault();
22 |
23 | // Toggle the targeted dropdown
24 | $dropdownContainer.toggleClass("open");
25 |
26 | // Refresh current scroll and add a min-height so we can reach the dropdown if needed
27 | self.components.dropdown.containerMinHeight.call(self, $dropdownContainer, $dropdownToggle);
28 |
29 | // Skip everything if we are in a dropdown toggling a dropdown (one click event is enuff!)
30 | if( $dropdownContainer.parents('.open > .dropdown-menu').length > 0 ) {
31 | return;
32 | }
33 |
34 | // Remove all other active dropdowns
35 | $('.open [data-toggle="dropdown"]').not($dropdownToggle).parent().removeClass('open');
36 |
37 | if ( !self.settings.hasTouch ) {
38 | self.scroll.iScrolls.terminal.disable();
39 | }
40 |
41 | $(document).off("click"+namespace).on("click"+namespace, function(eve) {
42 | // Check if we scrolled (touch devices wont trigger this click event after scrolling so assume we didn't move)
43 | var moved = ( !self.settings.hasTouch ) ? self.scroll.currentActive.moved : false,
44 | $target = $(eve.target);
45 |
46 | if (
47 | !moved && // If scrolling did not move
48 | $target.parents(".ui-pnotify").length === 0 && // if not a click within notifiaction
49 | (
50 | !$target.parents().is($dropdownContainer) || // if clicks are not made within the dropdown container
51 | $target.is('a:not([data-toggle="dropdown"])') // Unless it's a link but not a [data-toggle]
52 | )
53 | ) {
54 | $(document).off(eve);
55 | $dropdownContainer.removeClass('open');
56 |
57 | if ( !self.settings.hasTouch ) {
58 | $('.octoprint-container').css("min-height", 0);
59 | self.scroll.currentActive.refresh();
60 | self.scroll.iScrolls.terminal.enable();
61 | }
62 | }
63 | });
64 | });
65 |
66 | },
67 |
68 | // Support 1.3.0 onMouseOver dropdowns
69 | toggleSubmenu: function() {
70 | $(".dropdown-submenu").addClass("dropdown");
71 | $(".dropdown-submenu > a").attr("data-toggle", "dropdown");
72 | },
73 |
74 | // Refresh current scroll and add a min-height so we can reach the dropdown if needed
75 | containerMinHeight: function($dropdownContainer, $dropdownToggle) {
76 | var self = this;
77 |
78 | // Touch devices can reach the dropdown by CSS, only if we're using iScroll
79 | if ( !self.settings.hasTouch ) {
80 | // Get active container
81 | var $container = ($dropdownContainer.parents('.modal').length === 0 ) ? $('.octoprint-container') : $dropdownContainer.parents('.modal .modal-body');
82 |
83 | // If we toggle within the dropdown then get the parent dropdown for total height
84 | var $dropdownMenu = ( $dropdownContainer.parents('.open > .dropdown-menu').length > 0 ) ? $dropdownContainer.parents('.open > .dropdown-menu') : $dropdownToggle.next();
85 |
86 | setTimeout(function() {
87 |
88 | //If the main dropdown has closed (by toggle) then let's remove the min-height else
89 | if(!$dropdownMenu.parent().hasClass("open")) {
90 | $container.css("min-height", 0);
91 | self.scroll.currentActive.refresh();
92 | } else {
93 | var y = Math.abs(self.scroll.currentActive.y),
94 | height = $dropdownMenu.outerHeight(),
95 | top = $dropdownMenu.offset().top;
96 |
97 | $container.css("min-height", y + top + height);
98 | self.scroll.currentActive.refresh();
99 | }
100 |
101 | }, 0);
102 | }
103 | }
104 |
105 | }
106 |
--------------------------------------------------------------------------------
/source/js/components/fullscreen.js:
--------------------------------------------------------------------------------
1 | TouchUI.prototype.components.fullscreen = {
2 | init: function() {
3 | var self = this;
4 |
5 | // Bind fullscreenChange to knockout
6 | $(document).bind("fullscreenchange", function() {
7 | self.settings.isFullscreen($(document).fullScreen() !== false);
8 | self.DOM.storage.set("fullscreen", self.settings.isFullscreen());
9 | });
10 |
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/source/js/components/keyboard.js:
--------------------------------------------------------------------------------
1 | TouchUI.prototype.components.keyboard = {
2 |
3 | isActive: ko.observable(false),
4 | config: {
5 |
6 | default: {
7 | usePreview: false,
8 | autoAccept: true,
9 |
10 | display: {
11 | 'accept' : 'Save',
12 | 'bksp' : ' ',
13 | 'default': 'ABC',
14 | 'meta1' : '.?123',
15 | 'meta2' : '#+='
16 | },
17 |
18 | layout: 'custom',
19 | customLayout: {
20 | 'default': [
21 | 'q w e r t y u i o p',
22 | 'a s d f g h j k l',
23 | '{bksp} {s} z x c v b n m',
24 | '{accept} {c} {left} {right} {meta1} {space}'
25 | ],
26 | 'shift': [
27 | 'Q W E R T Y U I O P',
28 | 'A S D F G H J K L',
29 | '{bksp} {s} Z X C V B N M',
30 | '{accept} {c} {left} {right} {meta1} {space}'
31 | ],
32 | 'meta1': [
33 | '1 2 3 4 5 6 7 8 9 0',
34 | '- / : ; ( ) \u20ac & @',
35 | '{bksp} {meta2} . , ? ! \' "',
36 | '{accept} {c} {left} {right} {default} {space}'
37 | ],
38 | 'meta2': [
39 | '[ ] { } # % ^ * + =',
40 | '_ \\ | ~ < > $ \u00a3 \u00a5',
41 | '{bksp} {meta1} . , ? ! \' "',
42 | '{accept} {c} {left} {right} {default} {space}'
43 | ]
44 | }
45 |
46 | },
47 | terminal: {
48 | usePreview: false,
49 | autoAccept: true,
50 |
51 | display: {
52 | 'bksp' : ' ',
53 | 'accept' : 'Save',
54 | 'default': 'ABC',
55 | 'meta1' : '.?123',
56 | 'meta2' : '#+='
57 | },
58 |
59 | layout: 'custom',
60 | customLayout: {
61 | 'default': [
62 | 'Q W E R T Y U I O P',
63 | 'A S D F G H J K L',
64 | '{bksp} {s} Z X C V B N M',
65 | '{accept} {c} {left} {right} {meta1} {space}'
66 | ],
67 | 'meta1': [
68 | '1 2 3 4 5 6 7 8 9 0',
69 | '- / : ; ( ) \u20ac & @',
70 | '{bksp} {meta2} . , ? ! \' "',
71 | '{accept} {c} {left} {right} {default} {space}'
72 | ],
73 | 'meta2': [
74 | '[ ] { } # % ^ * + =',
75 | '_ \\ | ~ < > $ \u00a3 \u00a5',
76 | '{bksp} {meta1} . , ? ! \' "',
77 | '{accept} {c} {left} {right} {default} {space}'
78 | ]
79 | }
80 |
81 | },
82 | number: {
83 | usePreview: false,
84 | autoAccept: true,
85 |
86 | display: {
87 | 'bksp' : ' ',
88 | 'a' : 'Save',
89 | 'c' : 'Cancel'
90 | },
91 |
92 | layout: 'custom',
93 | customLayout: {
94 | 'default' : [
95 | '{bksp} 1 2 3 4 5 6 7 ',
96 | '{accept} {c} 8 9 0 - , . '
97 | ]
98 | },
99 | }
100 |
101 |
102 | },
103 |
104 | init: function() {
105 | var self = this;
106 |
107 | // Add virtual keyboard
108 | var obj = {
109 | visible: self.components.keyboard.onShow.bind(self),
110 | beforeClose: self.components.keyboard.onClose
111 | };
112 |
113 | var notThis = ['[type="file"]','[type="checkbox"]','[type="radio"]'];
114 | $(document).on("mousedown", 'input:not('+notThis+'), textarea', function(e) {
115 | var $elm = $(e.target);
116 |
117 | if(!self.components.keyboard.isActive()) {
118 |
119 | if($elm.data("keyboard")) {
120 | $elm.data("keyboard").close().destroy();
121 | }
122 |
123 | } else {
124 | // $elm already has a keyboard
125 | if($elm.data("keyboard")) {
126 | $elm.data('keyboard').reveal();
127 | return;
128 | }
129 |
130 | if($elm.attr("type") === "number") {
131 | $elm.keyboard($.extend(self.components.keyboard.config.number, obj));
132 | } else if($elm.attr("id") === "terminal-command") {
133 | $elm.keyboard($.extend(self.components.keyboard.config.terminal, obj));
134 | } else {
135 | $elm.keyboard($.extend(self.components.keyboard.config.default, obj));
136 | }
137 | }
138 |
139 | });
140 | },
141 |
142 | onShow: function(event, keyboard, el) {
143 | var self = this;
144 |
145 | keyboard.$keyboard.find("button").on("mousedown, touchstart", function(e) {
146 | var $elm = $(e.target);
147 | $elm.addClass("touch-focus");
148 |
149 | if($elm.data("timeout")) {
150 | clearTimeout($elm.data("timeout"));
151 | }
152 |
153 | var timeout = setTimeout(function() {
154 | $elm.removeClass("touch-focus").data("timeout", "");
155 | }, 1000);
156 |
157 | $elm.data("timeout", timeout);
158 | });
159 |
160 | if(!self.settings.hasTouch) {
161 | var height = keyboard.$keyboard.height();
162 | $('#page-container-main').css('padding-bottom', height);
163 |
164 | // Force iScroll to stop following the mouse (bug)
165 | self.scroll.currentActive._end(new Event('click'));
166 | setTimeout(function() {
167 | self.scroll.currentActive.scrollToElement(keyboard.$el[0], 200, 0, -30);
168 | }, 100);
169 |
170 | }
171 | },
172 |
173 | onClose: function(event, keyboard, el) {
174 | keyboard.$keyboard.find("button").off("mousedown, touchstart");
175 | $('#page-container-main').css('padding-bottom', 0);
176 | }
177 |
178 | }
179 |
--------------------------------------------------------------------------------
/source/js/components/modal.js:
--------------------------------------------------------------------------------
1 | TouchUI.prototype.components.modal = {
2 |
3 | init: function() {
4 | if($("#settings_dialog_menu").length > 0) {
5 | this.components.modal.dropdown.create.call(this, "#settings_dialog_menu", "special-dropdown-uni", "#settings_dialog_label");
6 | }
7 | if($("#usersettings_dialog ul.nav").length > 0) {
8 | this.components.modal.dropdown.create.call(this, "#usersettings_dialog ul.nav", "special-dropdown-uni-2", "#usersettings_dialog h3");
9 | }
10 | },
11 |
12 | dropdown: {
13 | create: function(cloneId, newId, appendTo) {
14 | var self = this;
15 |
16 | // Remove unwanted whitespaces
17 | $(appendTo).text($(appendTo).text().trim());
18 |
19 | // Create a label that is clickable
20 | var $settingsLabel = $(" ")
21 | .addClass("hidden")
22 | .attr("id", newId)
23 | .appendTo(appendTo)
24 | .text($(cloneId+" .active").text().trim())
25 | .on("click", function(e) {
26 |
27 | // Stop if we clicked on the dropdown and stop the dropdown from regenerating more then once
28 | if(e.target !== this || (e.target === this && $(".show-dropdown").length > 0)) {
29 | return;
30 | }
31 |
32 | // Clone the main settings menu
33 | var elm = $(cloneId)
34 | .clone()
35 | .attr("id", "")
36 | .appendTo(this)
37 | .addClass("show-dropdown");
38 |
39 | // Add click binder to close down the dropdown
40 | $(document).on("click", function(event) {
41 |
42 | if(
43 | $(event.target).closest('[data-toggle="tab"]').length > 0 || //Check if we clicked on a tab-link
44 | $(event.target).closest("#"+newId).length === 0 //Check if we clicked outside the dropdown
45 | ) {
46 | var href = $settingsLabel.find(".active").find('[data-toggle="tab"]').attr("href");
47 | $(document).off(event).trigger("dropdown-closed.touchui"); // Trigger event for enabling scrolling
48 |
49 | $('.show-dropdown').remove();
50 | $('[href="'+href+'"]').click();
51 | $settingsLabel.text($('[href="'+href+'"]').text());
52 |
53 | if( !self.settings.hasTouch ) {
54 | setTimeout(function() {
55 | self.scroll.modal.stack[self.scroll.modal.stack.length-1].refresh();
56 | }, 0);
57 | }
58 | }
59 |
60 | });
61 |
62 | // Trigger event for disabling scrolling
63 | $(document).trigger("dropdown-open.touchui", elm[0]);
64 | });
65 |
66 | // reset the active text in dropdown on open
67 | $(appendTo)
68 | .closest(".modal")
69 | .on("modal.touchui", function() {
70 | var href = $(cloneId)
71 | .find(".active")
72 | .find('[data-toggle="tab"]')
73 | .attr("href");
74 |
75 | $settingsLabel.text($('[href="'+href+'"]').text());
76 | });
77 |
78 | }
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/source/js/components/slider.js:
--------------------------------------------------------------------------------
1 | TouchUI.prototype.components.slider = {
2 |
3 | init: function() {
4 |
5 | ko.bindingHandlers.slider = {
6 | init: function (element, valueAccessor) {
7 | var $element = $(element);
8 |
9 | // Set value on input field
10 | $element.val(valueAccessor().value());
11 |
12 | // Create container
13 | var div = $('
').insertBefore(element);
14 |
15 | // Wait untill next DOM bindings are executed
16 | setTimeout(function() {
17 | var $button = $(element).next('button');
18 | var id = _.uniqueId("ui-inp");
19 | var text = $button.text().split(":")[0].replace(" ", "");
20 |
21 | $button.appendTo(div);
22 | $element.appendTo(div);
23 |
24 | $(div).find('input').attr("id", id);
25 |
26 | var lbl = $('' + text + ': ');
27 | lbl.appendTo('.octoprint-container')
28 | $element.attr("style", "padding-left:" + (lbl.width() + 15) + "px");
29 | lbl.appendTo(div);
30 |
31 | if (valueAccessor().tools && valueAccessor().tools.length === 0) {
32 | div.hide();
33 | } else {
34 | div.show();
35 | }
36 |
37 | }, 60);
38 |
39 | $element.on("change", function(e) {
40 | valueAccessor().value(parseFloat($element.val()));
41 | }).attr({
42 | max: valueAccessor().max,
43 | min: valueAccessor().min,
44 | step: valueAccessor().step,
45 | });
46 |
47 | },
48 | update: function (element, valueAccessor) {
49 | $(element).val(parseFloat(valueAccessor().value()));
50 |
51 | if (valueAccessor().tools && valueAccessor().tools.length === 0) {
52 | $(element).parent().hide();
53 | } else {
54 | $(element).parent().show();
55 | }
56 | }
57 | };
58 |
59 | }
60 |
61 | }
62 |
--------------------------------------------------------------------------------
/source/js/components/touch-list.js:
--------------------------------------------------------------------------------
1 | TouchUI.prototype.components.touchList = {
2 | init: function() {
3 |
4 | /* Add touch friendly files list */
5 | var self = this;
6 | var touch = false;
7 | var start = 0;
8 | var namespace = ".files.touchui";
9 |
10 | $(document).on("mousedown touchstart", "#files .entry:not(.back), #temp .row-fluid", function(e) {
11 | try {
12 | touch = e.currentTarget;
13 | start = e.pageX || e.originalEvent.targetTouches[0].pageX;
14 | } catch(err) {
15 | return;
16 | }
17 |
18 | $(document).one("mouseup"+namespace+" touchend"+namespace, function(e) {
19 | touch = false;
20 | start = 0;
21 |
22 | $(document).off(namespace);
23 | });
24 |
25 | $(document).on("mousemove"+namespace+" touchmove"+namespace, function(event) {
26 | if(touch !== false) {
27 | try {
28 | var current = event.pageX || event.originalEvent.targetTouches[0].pageX;
29 |
30 | if(current > start + 80) {
31 | $(document).trigger("fileclose" + namespace, event.target);
32 | $(touch).removeClass("open");
33 | start = current;
34 | } else if(current < start - 80) {
35 | $(document).trigger("fileopen" + namespace, event.target);
36 | $(touch).addClass("open");
37 | start = current;
38 |
39 | if( $(touch).find(".btn-group").children().length > 4 ) {
40 | $(touch).addClass("large");
41 | }
42 | }
43 | } catch(err) {
44 | //Ignore step
45 | }
46 | }
47 | });
48 |
49 | });
50 |
51 | }
52 |
53 | }
54 |
--------------------------------------------------------------------------------
/source/js/components/touchscreen.js:
--------------------------------------------------------------------------------
1 | TouchUI.prototype.components.touchscreen = {
2 |
3 | init: function () {
4 | $("html").addClass("isTouchscreenUI");
5 |
6 | if (this.settings.isEpiphanyOrKweb) {
7 | this.settings.hasTouch = false;
8 | this.scroll.defaults.iScroll.disableTouch = true;
9 | this.scroll.defaults.iScroll.disableMouse = false;
10 | }
11 |
12 | this.settings.isTouchscreen(true);
13 |
14 | if (this.settings.hasBootloader) {
15 | this.settings.hasFullscreen(false);
16 | }
17 |
18 | $('.modal.fade').removeClass('fade');
19 | $('#gcode_link').remove();
20 |
21 | // Improve performace
22 | this.scroll.defaults.iScroll.scrollbars = false;
23 | this.scroll.defaults.iScroll.interactiveScrollbars = false;
24 | this.scroll.defaults.iScroll.useTransition = false;
25 | // this.scroll.defaults.iScroll.useTransform = false;
26 | // this.scroll.defaults.iScroll.HWCompositing = false;
27 |
28 | // Remove any links opening in a new tab
29 | $('[target="_blank"]').each(function(ind, elm) {
30 | $(elm)
31 | .attr("target", "")
32 | .on("click", function(e) {
33 | return confirm("Do you want to navigate away from TouchUI?");
34 | });
35 | });
36 |
37 | // disable dashboard layer progress
38 | if ($('.dashboardGridItem').length > 0) {
39 | $('.dashboardGridItem').each(function(i, elm) {
40 | if ($(elm).find('[title="Layer Progress"]').length > 0) {
41 | $(elm).remove();
42 | }
43 | });
44 | }
45 | },
46 |
47 | isLoading: function (viewModels) {
48 |
49 | if(this.settings.isTouchscreen()) {
50 | // Disable fancy functionality
51 | if(viewModels.terminalViewModel.enableFancyFunctionality) { //TODO: check if 1.2.9 to not throw errors in 1.2.8<
52 | viewModels.terminalViewModel.enableFancyFunctionality(false);
53 | }
54 |
55 | // Disable GCodeViewer in touchscreen mode
56 | if (viewModels.gcodeViewModel) {
57 | console.info("TouchUI: GCodeViewer is disabled while TouchUI is active and in touchscreen mode.");
58 | viewModels.gcodeViewModel.enabled = false;
59 | viewModels.gcodeViewModel.initialize = _.noop;
60 | viewModels.gcodeViewModel.clear = _.noop;
61 | viewModels.gcodeViewModel._processData = _.noop;
62 | }
63 | }
64 |
65 | }
66 |
67 | }
68 |
--------------------------------------------------------------------------------
/source/js/constructor.js:
--------------------------------------------------------------------------------
1 | var TouchUI = function() {
2 | this.core.init.call(this);
3 | this.knockout.viewModel.call(this);
4 | this.knockout.bindings.call(this);
5 | return this.core.bridge.call(this);
6 | };
7 |
8 | TouchUI.prototype = {
9 | constructor: TouchUI,
10 | isActive: ko.observable(false),
11 |
12 | settings: {
13 | id: "touch",
14 | version: 0,
15 | requiredBootloaderVersion: 1,
16 |
17 | isFullscreen: ko.observable(false),
18 | isTouchscreen: ko.observable(false),
19 |
20 | isEpiphanyOrKweb: window.navigator.userAgent.indexOf("AppleWebKit") !== -1 && window.navigator.userAgent.indexOf("ARM Mac OS X") !== -1,
21 | isChromiumArm: window.navigator.userAgent.indexOf("X11") !== -1 && window.navigator.userAgent.indexOf("Chromium") !== -1 && window.navigator.userAgent.indexOf("armv7l") !== -1,
22 |
23 | hasBootloader: window.navigator.userAgent.indexOf("TouchUI") !== -1,
24 | hasFullscreen: ko.observable(document.webkitCancelFullScreen || document.msCancelFullScreen || document.oCancelFullScreen || document.mozCancelFullScreen || document.cancelFullScreen),
25 | hasLocalStorage: ('localStorage' in window),
26 | hasTouch: ('ontouchstart' in window) || (navigator.maxTouchPoints > 0) || (navigator.msMaxTouchPoints > 0),
27 |
28 | canBoot: {
29 | resolution: $("#touchui_auto_load_resolution").length > 0,
30 | touch: $("#touchui_auto_load_touch").length > 0
31 | },
32 |
33 | whatsNew: ko.observable(false)
34 | },
35 |
36 | core: {},
37 | components: {},
38 | knockout: {},
39 | plugins: {},
40 | animate: {
41 | isHidebarActive: ko.observable(false)
42 | },
43 | DOM: {
44 | create: {},
45 | move: {},
46 | overwrite: {}
47 | },
48 | scroll: {
49 |
50 | defaults: {
51 | iScroll: {
52 | eventPassthrough: 'horizontal',
53 | scrollbars: true,
54 | mouseWheel: true,
55 | interactiveScrollbars: true,
56 | shrinkScrollbars: "scale",
57 | fadeScrollbars: true
58 | }
59 | },
60 |
61 | iScrolls: {},
62 | currentActive: null
63 | }
64 |
65 | }
66 |
--------------------------------------------------------------------------------
/source/js/core/_init.js:
--------------------------------------------------------------------------------
1 | TouchUI.prototype.core.init = function() {
2 |
3 | // Migrate old cookies into localstorage
4 | this.DOM.storage.migration.call(this);
5 |
6 | // Bootup TouchUI if Touch, Small resolution or storage say's so
7 | if (this.core.boot.call(this)) {
8 |
9 | // Send Touchscreen loading status
10 | if (window.top.postMessage) {
11 | window.top.postMessage("loading", "*");
12 | }
13 |
14 | // Attach id for TouchUI styling
15 | $("html").attr("id", this.settings.id);
16 |
17 | // Force mobile browser to set the window size to their format
18 | $(' ').appendTo("head");
19 | $(' ').appendTo("head");
20 | $(' ').appendTo("head");
21 |
22 | this.isActive(true);
23 |
24 | // Enforce active cookie
25 | this.DOM.storage.set("active", true);
26 |
27 | var isTouchDevice = this.settings.isEpiphanyOrKweb || this.settings.isChromiumArm || this.settings.hasBootloader;
28 |
29 | // Create keyboard cookie if not existing
30 | if (this.DOM.storage.get("keyboardActive") === undefined) {
31 | if (!this.settings.hasTouch || isTouchDevice) {
32 | this.DOM.storage.set("keyboardActive", true);
33 | } else {
34 | this.DOM.storage.set("keyboardActive", false);
35 | }
36 | }
37 |
38 | // Create hide navbar on click if not existing
39 | if (this.DOM.storage.get("hideNavbarActive") === undefined) {
40 | this.DOM.storage.set("hideNavbarActive", false);
41 | }
42 |
43 | // Treat KWEB3 as a special Touchscreen mode or enabled by cookie
44 | if (
45 | this.DOM.storage.get("touchscreenActive") ||
46 | (
47 | isTouchDevice &&
48 | this.DOM.storage.get("touchscreenActive") === undefined
49 | )
50 | ) {
51 | this.components.touchscreen.init.call(this);
52 | }
53 |
54 | // If TouchUI has been started through bootloader then initialize the process during reloads
55 | if (this.settings.hasBootloader && window.top.postMessage) {
56 | window.onbeforeunload = function() {
57 | window.top.postMessage("reset", "*");
58 | };
59 | }
60 |
61 | // Get state of cookies and store them in KO
62 | this.components.keyboard.isActive(this.DOM.storage.get("keyboardActive"));
63 | this.animate.isHidebarActive(this.DOM.storage.get("hideNavbarActive"));
64 | this.settings.isFullscreen($(document).fullScreen() !== false);
65 | }
66 |
67 | }
68 |
--------------------------------------------------------------------------------
/source/js/core/boot.js:
--------------------------------------------------------------------------------
1 | TouchUI.prototype.core.boot = function() {
2 |
3 | // This should always start TouchUI
4 | if(
5 | document.location.hash === "#touch" ||
6 | document.location.href.indexOf("?touch") > 0 ||
7 | this.DOM.storage.get("active") ||
8 | this.settings.hasBootloader
9 | ) {
10 |
11 | return true;
12 |
13 | } else if(
14 | this.DOM.storage.get("active") !== false
15 | ) {
16 |
17 | if($(window).width() < 980 && this.settings.canBoot.resolution) {
18 | return true;
19 | }
20 |
21 | if(this.settings.hasTouch && this.settings.canBoot.touch) {
22 | return true;
23 | }
24 |
25 | }
26 |
27 | return false;
28 |
29 | }
30 |
--------------------------------------------------------------------------------
/source/js/core/bridge.js:
--------------------------------------------------------------------------------
1 | TouchUI.prototype.core.bridge = function() {
2 | var self = this;
3 |
4 | this.core.bridge = {
5 |
6 | allViewModels: {},
7 | TOUCHUI_REQUIRED_VIEWMODELS: [
8 | "terminalViewModel",
9 | "connectionViewModel",
10 | "settingsViewModel",
11 | "softwareUpdateViewModel",
12 | "controlViewModel",
13 | "gcodeFilesViewModel",
14 | "navigationViewModel",
15 | "pluginManagerViewModel",
16 | "temperatureViewModel",
17 | "loginStateViewModel"
18 | ],
19 | TOUCHUI_ELEMENTS: [
20 | "#touchui_settings_dialog",
21 | "#settings_plugin_touchui",
22 | "#navbar_plugin_touchui"
23 | ],
24 |
25 | domLoading: function() {
26 | if (self.isActive()) {
27 | self.scroll.beforeLoad.call(self);
28 | self.DOM.init.call(self);
29 |
30 | if (moment && moment.locale) {
31 | // Overwrite the 'moment.locale' fuction and call original:
32 | // The purpose is that we want to remove data before
33 | // registering it to OctoPrint. Moment.locale is called
34 | // just before this happens.
35 | var old = moment.locale;
36 | moment.locale = function() {
37 | self.plugins.disable.init.call(self);
38 | old.apply(moment, arguments);
39 | };
40 | }
41 | }
42 | },
43 |
44 | domReady: function() {
45 | if (self.isActive()) {
46 |
47 | if($("#gcode").length > 0) {
48 | self.core.bridge.TOUCHUI_REQUIRED_VIEWMODELS = self.core.bridge.TOUCHUI_REQUIRED_VIEWMODELS.concat(["gcodeViewModel"]);
49 | }
50 |
51 | self.components.dropdown.init.call(self);
52 | self.components.fullscreen.init.call(self);
53 | self.components.keyboard.init.call(self);
54 | self.components.modal.init.call(self);
55 | self.components.touchList.init.call(self);
56 | self.components.slider.init.call(self);
57 |
58 | self.scroll.init.call(self);
59 | }
60 | },
61 |
62 | koStartup: function TouchUIViewModel(viewModels) {
63 | self.core.bridge.allViewModels = _.object(self.core.bridge.TOUCHUI_REQUIRED_VIEWMODELS, viewModels);
64 | self.knockout.isLoading.call(self, self.core.bridge.allViewModels);
65 | return self;
66 | }
67 | }
68 |
69 | return this.core.bridge;
70 | }
71 |
--------------------------------------------------------------------------------
/source/js/core/less.js:
--------------------------------------------------------------------------------
1 | TouchUI.prototype.core.less = {
2 |
3 | options: {
4 | template: {
5 | importUrl: "./plugin/touchui/static/less/touchui.bundled.less?t=" + new Date().getTime(),
6 | import: '@import "{importUrl}"; \n',
7 | variables: "@main-color: {mainColor}; \n" +
8 | "@terminal-color: {termColor}; \n" +
9 | "@text-color: {textColor}; \n" +
10 | "@main-font-size: {fontSize}px; \n" +
11 | "@main-background: {bgColor}; \n\n"
12 | },
13 | API: "./plugin/touchui/css"
14 | },
15 |
16 | save: function() {
17 | var options = this.core.less.options;
18 | var self = this;
19 |
20 | if(self.settings.useCustomization()) {
21 | if(self.settings.colors.useLocalFile()) {
22 |
23 | $.get(options.API, {
24 | path: self.settings.colors.customPath()
25 | })
26 | .done(function(response) {
27 | self.core.less.render.call(self, options.template.import.replace("{importUrl}", options.template.importUrl) + response);
28 | })
29 | .error(function(error) {
30 | self.core.less.error.call(self, error);
31 | });
32 |
33 | } else {
34 |
35 | self.core.less.render.call(self, "" +
36 | options.template.import.replace("{importUrl}", options.template.importUrl) +
37 | options.template.variables.replace("{mainColor}", self.settings.colors.mainColor())
38 | .replace("{termColor}", self.settings.colors.termColor())
39 | .replace("{textColor}", self.settings.colors.textColor())
40 | .replace("{bgColor}", self.settings.colors.bgColor())
41 | .replace("{fontSize}", self.settings.colors.fontSize())
42 | );
43 |
44 | }
45 | }
46 | },
47 |
48 | render: function(data) {
49 | var self = this;
50 | var callback = function(error, result) {
51 |
52 | if (error) {
53 | self.core.less.error.call(self, { responseText: 'Less parser: ' + error.message, status: 0 });
54 | console.error(error);
55 | } else {
56 | result.css = result.css.replace(/mixin\:placeholder\;/g, '');
57 |
58 | $.post(self.core.less.options.API, {
59 | css: result.css
60 | })
61 | .done(function() {
62 | self.settings.refreshCSS(true);
63 | $(window).trigger('resize');
64 | })
65 | .fail(function(error) {
66 | self.core.less.error.call(self, error);
67 | });
68 |
69 | }
70 | }
71 |
72 | if(window.less.render) {
73 | window.less.render(data, {
74 | compress: true
75 | }, callback);
76 | } else {
77 | window.less.Parser({}).parse(data, function(error, result) {
78 | if(result) {
79 | result = {
80 | css: result.toCSS({
81 | compress: true
82 | })
83 | }
84 | }
85 | callback.call(this, error, result);
86 | });
87 | }
88 | },
89 |
90 | error: function(error) {
91 | var content = error.responseText;
92 | if(content && content.trim() && error.status !== 401) {
93 | new PNotify({
94 | title: 'TouchUI: Whoops, something went wrong...',
95 | text: content,
96 | icon: 'glyphicon glyphicon-question-sign',
97 | type: 'error',
98 | hide: false
99 | });
100 | }
101 |
102 | }
103 |
104 | }
105 |
--------------------------------------------------------------------------------
/source/js/core/version.js:
--------------------------------------------------------------------------------
1 | TouchUI.prototype.core.version = {
2 |
3 | init: function(softwareUpdateViewModel) {
4 | var self = this;
5 |
6 | $(" ").appendTo("#terminal-output");
7 |
8 | if(softwareUpdateViewModel) {
9 |
10 | softwareUpdateViewModel.versions.items.subscribe(function(changes) {
11 |
12 | var touchui = softwareUpdateViewModel.versions.getItem(function(elm) {
13 | return (elm.key === "touchui");
14 | }, true) || false;
15 |
16 | if( touchui !== false && touchui.information !== null ) {
17 | var remote = Number(touchui.information.remote.value.split('.').join('')),
18 | local = Number(touchui.information.local.value.split('.').join(''));
19 |
20 | if(remote > local) {
21 | $("#touch_updates_css").remove();
22 | $('head').append('');
23 | } else {
24 | if( $("#touch_updates_css").length === 0 ) {
25 | $('head').append('');
26 | }
27 | }
28 | }
29 |
30 | });
31 |
32 | }
33 |
34 | }
35 |
36 | }
37 |
--------------------------------------------------------------------------------
/source/js/dom/_init.js:
--------------------------------------------------------------------------------
1 | TouchUI.prototype.DOM.init = function() {
2 |
3 | // Create new tab with printer status and make it active
4 | this.DOM.create.printer.init(this.DOM.create.tabbar);
5 | this.DOM.create.printer.menu.$elm.find('a').trigger("click");
6 |
7 | // Create a new persistent dropdown
8 | this.DOM.create.dropdown.init.call( this.DOM.create.dropdown );
9 |
10 | // Add a webcam tab if it's defined
11 | if ($("#webcam_container").length > 0) {
12 | this.DOM.create.webcam.init(this.DOM.create.tabbar);
13 | }
14 |
15 | // Move all other items from tabbar into dropdown
16 | this.DOM.move.sidebar.init.call(this);
17 | this.DOM.move.navbar.init.call(this);
18 | this.DOM.move.tabbar.init.call(this);
19 | this.DOM.move.afterTabAndNav.call(this);
20 | this.DOM.move.overlays.init.call(this);
21 | this.DOM.move.terminal.init.call(this);
22 |
23 | // Move connection sidebar into a new modal
24 | this.DOM.move.connection.init(this.DOM.create.tabbar);
25 |
26 | // Manipulate controls div
27 | this.DOM.move.controls.init();
28 |
29 | // Disable these bootstrap/jquery plugins
30 | this.DOM.overwrite.tabdrop.call(this);
31 | this.DOM.overwrite.modal.call(this);
32 | this.DOM.overwrite.pnotify.call(this);
33 |
34 | // Add class with how many tab-items
35 | $("#tabs, #navbar").addClass("items-" + $("#tabs li:not(.hidden_touch)").length);
36 |
37 | // Remove active class when clicking on a tab in the tabbar
38 | $('#tabs [data-toggle=tab]').on("click", function() {
39 | $("#all_touchui_settings").removeClass("item_active");
40 | });
41 |
42 | // If touch emulator is enabled, then disable dragging of a menu item for scrolling
43 | if(!this.settings.hasTouch) {
44 | $("#navbar ul.nav > li a").on("dragstart drop", function(e) {
45 | return false;
46 | });
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/source/js/dom/cookies.js:
--------------------------------------------------------------------------------
1 | TouchUI.prototype.DOM.cookies = {
2 |
3 | get: function(key, isPlain) {
4 | var name = (isPlain) ? key + "=" : "TouchUI." + key + "=";
5 | var ca = document.cookie.split(';');
6 | var tmp;
7 | for(var i=0; i' +
12 | '' +
13 | $('navbar_show_settings').text() || $('navbar_show_settings').attr("title") +
14 | ' ' +
15 | '').prependTo(this.menuItem.cloneTo);
16 |
17 | this.container = $('').appendTo(this.menuItem.menu);
18 | }
19 |
20 | }
21 |
--------------------------------------------------------------------------------
/source/js/dom/create/printer.js:
--------------------------------------------------------------------------------
1 | TouchUI.prototype.DOM.create.printer = {
2 |
3 | menu: {
4 | cloneTo: "#tabs"
5 | },
6 |
7 | container: {
8 | cloneTo: "#temp"
9 | },
10 |
11 | move: {
12 | $state: $("#state_wrapper"),
13 | $files: $("#files_wrapper")
14 | },
15 |
16 | init: function( tabbar ) {
17 | this.menu.$elm = tabbar.createItem("print_link", "printer", "tab").prependTo(this.menu.cloneTo);
18 | this.container.$elm = $('').insertBefore(this.container.cloneTo);
19 |
20 | // Move the contents of the hidden accordions to the new print status and files tab
21 | this.move.$state.appendTo(this.container.$elm.find(".row-fluid"));
22 | this.move.$files.insertAfter(this.container.$elm.find(".row-fluid #state_wrapper"));
23 |
24 | // Create an upload button in the header
25 | $('')
30 | .appendTo('#files_wrapper .accordion-heading')
31 | .find('a[href="#"]')
32 | .on('click', function(e) {
33 | e.preventDefault();
34 | $('#gcode_upload').click();
35 | });
36 | }
37 |
38 | }
39 |
--------------------------------------------------------------------------------
/source/js/dom/create/tabbar.js:
--------------------------------------------------------------------------------
1 | TouchUI.prototype.DOM.create.tabbar = {
2 |
3 | createItem: function(itemId, linkId, toggle, text) {
4 | text = (text) ? text : "";
5 | return $(''+text+' ');
6 |
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/source/js/dom/create/webcam.js:
--------------------------------------------------------------------------------
1 | TouchUI.prototype.DOM.create.webcam = {
2 |
3 | menu: {
4 | webcam: {
5 | cloneTo: "#tabs #control_link"
6 | }
7 | },
8 |
9 | container: {
10 | cloneTo: "#tabs + .tab-content",
11 |
12 | webcam: {
13 | $container: $("#webcam_container"),
14 | cloneTo: "#webcam"
15 | }
16 | },
17 |
18 | init: function( tabbar ) {
19 | this.container.$elm = $('
').appendTo(this.container.cloneTo);
20 | this.menu.webcam.$elm = tabbar.createItem("webcam_link", "webcam", "tab").insertAfter(this.menu.webcam.cloneTo).find('a').text('Webcam');
21 |
22 | this.container.webcam.$container.next().appendTo(this.container.webcam.cloneTo);
23 | this.container.webcam.$container.prependTo(this.container.webcam.cloneTo);
24 |
25 | $('').insertBefore(this.container.$elm);
26 | $('').insertAfter(this.container.$elm);
27 |
28 | $("#webcam_container").attr("data-bind", $("#webcam_container").attr("data-bind").replace("keydown: onKeyDown, ", ""));
29 | $("#webcam_image").on("mousedown", function(e) {
30 | e.preventDefault();
31 | });
32 | }
33 |
34 | }
35 |
--------------------------------------------------------------------------------
/source/js/dom/localstorage.js:
--------------------------------------------------------------------------------
1 | TouchUI.prototype.DOM.localstorage = {
2 | store: JSON.parse(localStorage["TouchUI"] || "{}"),
3 |
4 | get: function (key) {
5 | return this.store[key];
6 | },
7 |
8 | set: function (key, value) {
9 | this.store[key] = value;
10 | localStorage["TouchUI"] = JSON.stringify(this.store);
11 | return this.store[key];
12 | },
13 |
14 | toggleBoolean: function (key) {
15 | var value = this.store[key] || false;
16 |
17 | if(value === true) {
18 | this.set(key, false);
19 | } else {
20 | this.set(key, true);
21 | }
22 |
23 | return !value;
24 |
25 | }
26 |
27 | }
28 |
--------------------------------------------------------------------------------
/source/js/dom/move/afterTabAndNav.js:
--------------------------------------------------------------------------------
1 | TouchUI.prototype.DOM.move.afterTabAndNav = function() {
2 |
3 | this.DOM.create.dropdown.container.children().each(function(ind, elm) {
4 | var $elm = $(elm);
5 | $('').insertBefore($elm);
6 | $('').insertAfter($elm);
7 | });
8 |
9 | //Add hr before the settings icon
10 | $(' ').insertBefore("#navbar_settings");
11 | $('').insertBefore("#navbar_systemmenu").attr("data-bind", $("#navbar_systemmenu").attr("data-bind"));
12 |
13 | if ($("#touchui_text_nonlink_container").length > 0) {
14 | $(' ').insertBefore($("#touchui_text_nonlink_container").parent());
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/source/js/dom/move/connection.js:
--------------------------------------------------------------------------------
1 | TouchUI.prototype.DOM.move.connection = {
2 | $container: null,
3 | containerId: "connection_dialog",
4 | $cloneContainer: $("#usersettings_dialog"),
5 | $cloneModal: $("#connection_wrapper"),
6 | cloneTo: "#all_touchui_settings > ul",
7 |
8 | init: function( tabbar ) {
9 | var text = this.$cloneModal.find(".accordion-heading").text().trim();
10 |
11 | // Clone usersettings modal
12 | this.$container = this.$cloneContainer.clone().attr("id", this.containerId).insertAfter(this.$cloneContainer);
13 | this.$containerBody = this.$container.find(".modal-body");
14 |
15 | // Remove all html from clone
16 | this.$containerBody.html("");
17 |
18 | // Append tab contents to modal
19 | this.$cloneModal.appendTo(this.$containerBody);
20 |
21 | // Set modal header to accordion header
22 | this.$container.find(".modal-header h3").text(text);
23 |
24 | // Create a link in the dropdown
25 | this.$menuItem = tabbar.createItem("conn_link2", this.containerId, "modal", text)
26 | .attr("data-bind", "visible: loginState.isAdmin")
27 | .prependTo(this.cloneTo);
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/source/js/dom/move/controls.js:
--------------------------------------------------------------------------------
1 | TouchUI.prototype.DOM.move.controls = {
2 |
3 | init: function() {
4 |
5 | if($('#control-jog-feedrate .input-append').length === 0) {
6 | // <1.4.1
7 | $("#control-jog-feedrate").insertBefore("#control-jog-extrusion");
8 | $("#control-jog-extrusion button:last-child").prependTo("#control-jog-feedrate");
9 | $("#control-jog-extrusion input:last-child").attr('data-bind', $("#control-jog-extrusion input:last-child").attr('data-bind').replace('slider: {', 'slider: {tools: tools(), ')).prependTo("#control-jog-feedrate");
10 | $("#control-jog-extrusion .slider:last-child").prependTo("#control-jog-feedrate");
11 | }
12 |
13 | // Move Z-panel
14 | $("#control-jog-general").insertAfter("#control-jog-z");
15 |
16 | // Create panel
17 | var $jog = $('
').attr('id', 'control-jog-rate').insertBefore('#control-jog-extrusion');
18 | $("#control div.distance").appendTo($jog);
19 | $("#control-jog-feedrate").appendTo($jog);
20 | $("#control-jog-flowrate").appendTo($jog);
21 | }
22 |
23 | }
24 |
--------------------------------------------------------------------------------
/source/js/dom/move/navbar.js:
--------------------------------------------------------------------------------
1 | TouchUI.prototype.DOM.move.navbar = {
2 | mainItems: ['#all_touchui_settings', '#navbar_login', '.hidden_touch', '#touchui_auto_load_touch', '#touchui_auto_load_resolution'],
3 | init: function() {
4 |
5 | var $items = $("#navbar ul.nav > li:not("+this.DOM.move.navbar.mainItems+")");
6 | var hasTextLinks = false;
7 | $($items.get().reverse()).each(function(ind, elm) {
8 | var $elm = $(elm);
9 |
10 | if($elm.children('a').length > 0) {
11 | var elme = $elm.children('a')[0];
12 |
13 | $elm.prependTo(this.DOM.create.dropdown.container);
14 |
15 | $.each(elme.childNodes, function(key, node) {
16 | if(node.nodeName === "#text") {
17 | node.nodeValue = node.nodeValue.trim();
18 | }
19 | });
20 |
21 | if(!$(elme).text()) {
22 | if(!$(elme).text()) {
23 | $(elme).text($(elme).attr('title'));
24 | }
25 | }
26 | } else {
27 | if(!hasTextLinks) {
28 | hasTextLinks = true;
29 | $(' ').appendTo(this.DOM.create.dropdown.container);
30 | }
31 |
32 | $elm.appendTo("#touchui_text_nonlink_container");
33 | }
34 | }.bind(this));
35 |
36 | $(document).on('click', function(elm) {
37 | if($(elm.target).parents('#tabs').length > 0) {
38 | $('#tabs .itemActive').removeClass('itemActive');
39 | $(elm.target).addClass('itemActive');
40 | }
41 | });
42 |
43 | $('[href="'+document.location.hash+'"]').addClass('itemActive');
44 |
45 | // Move TouchUI to main dropdown
46 | $("#navbar_plugin_touchui").insertAfter("#navbar_settings");
47 |
48 | // Create and Move login form to main dropdown
49 | $(' ').insertAfter("#navbar_plugin_touchui");
50 |
51 | $('#navbar_login')
52 | .appendTo('#youcanhazlogin')
53 | .find('a.dropdown-toggle')
54 | .text($('#youcanhazlogin').find('a.dropdown-toggle').text().trim())
55 | .attr("data-bind", "visible: !loginState.loggedIn()");
56 |
57 | // Create a fake dropdown link that will be overlapped by settings icon
58 | $(' ').appendTo("#tabs");
59 |
60 | // Move the navbar temp plugin
61 | this.plugins.psuControl.call(this);
62 | }
63 |
64 | }
65 |
--------------------------------------------------------------------------------
/source/js/dom/move/overlays.js:
--------------------------------------------------------------------------------
1 | TouchUI.prototype.DOM.move.overlays = {
2 |
3 | mainItems: ['#offline_overlay', '#reloadui_overlay', '#drop_overlay'],
4 | init: function() {
5 |
6 | $(this.DOM.move.overlays.mainItems).each(function(ind, elm) {
7 | var $elm = $(elm);
8 | $elm.appendTo('body');
9 | }.bind(this));
10 |
11 | }
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/source/js/dom/move/sidebar.js:
--------------------------------------------------------------------------------
1 | TouchUI.prototype.DOM.move.sidebar = {
2 |
3 | items: ".octoprint-container > .row > .accordion > div",
4 |
5 | menu: {
6 | cloneTo: "#tabs"
7 | },
8 |
9 | container: {
10 | cloneTo: "#temp"
11 | },
12 |
13 | doNotMove: [
14 | '#sidebar_plugin_printer_safety_check_wrapper',
15 | '#connection_wrapper'
16 | ],
17 |
18 | init: function() {
19 | var tabbar = this.DOM.create.tabbar;
20 | $(this.DOM.move.sidebar.items + ':not(' + this.DOM.move.sidebar.doNotMove + ')').each(function(ind, elm) {
21 | var id = $(elm).attr('id');
22 |
23 | tabbar.createItem(id + "_link", id, "tab")
24 | .appendTo(this.menu.cloneTo)
25 | .find('[data-toggle="tab"]')
26 | .text($(elm).find('.accordion-toggle').text().trim());
27 |
28 | $('')
29 | .insertBefore(this.container.cloneTo)
30 | .children().get(0)
31 | .prepend(elm);
32 |
33 | }.bind(this.DOM.move.sidebar));
34 |
35 | }
36 |
37 | }
38 |
--------------------------------------------------------------------------------
/source/js/dom/move/tabbar.js:
--------------------------------------------------------------------------------
1 | TouchUI.prototype.DOM.move.tabbar = {
2 | init: function(viewModels) {
3 |
4 | var $items = $("#tabs > li:not(#print_link, #touchui_dropdown_link, .hidden_touch)");
5 | $($items.get().reverse()).each(function(ind, elm) {
6 | var $elm = $(elm);
7 |
8 | // Clone the items into the dropdown, and make it click the orginal link
9 | $elm
10 | .clone()
11 | .attr("id", $elm.attr("id")+"2")
12 | .removeAttr('style')
13 | .removeAttr('data-bind')
14 | .prependTo(this.DOM.create.dropdown.container)
15 | .find("a")
16 | .off("click")
17 | .on("click", function(e) {
18 | $elm.find('a').click();
19 | $("#all_touchui_settings").addClass("item_active");
20 | e.preventDefault();
21 | return false;
22 | });
23 |
24 | // $elm.addClass("hidden_touch");
25 | }.bind(this));
26 |
27 | $("#tabs > li > a").each(function(ind, elm) {
28 | $(elm).text("");
29 | });
30 |
31 | var resize = function() {
32 | var width = $('#print_link').width();
33 | var winWidth = $(window).width();
34 | var $items = $('#tabs > li:not("#touchui_dropdown_link")');
35 | var itemsFit = Math.floor(winWidth / width) - 2;
36 |
37 | // Loop over items; if they contain display: none; then do
38 | // not show them in the dropdown menu and filter them out from items
39 | $items = $items.filter(function(i, elm) {
40 | if (($(elm).attr('style') || "").indexOf('none') !== -1) {
41 | $('#' + $(elm).attr('id') + '2').addClass('hidden_touch');
42 | return false;
43 | }
44 |
45 | return true;
46 | });
47 |
48 | if (winWidth > (width * 2)) {
49 | $items.each(function(key, elm) {
50 | if (key > itemsFit) {
51 | $(elm).addClass('hidden_touch');
52 | $('#' + $(elm).attr('id') + '2').removeClass('hidden_touch');
53 | } else {
54 | $(elm).removeClass('hidden_touch');
55 | $('#' + $(elm).attr('id') + '2').addClass('hidden_touch');
56 | }
57 | });
58 | }
59 |
60 | // Sync width of dropdown link
61 | $('#all_touchui_settings').width(width);
62 | }
63 |
64 | $(window).on('resize.touchui.tabbar', resize);
65 | $(window).on('resize.touchui.tabbar', _.debounce(resize, 200));
66 | $(window).on('resize.touchui.tabbar', _.debounce(resize, 600));
67 |
68 | $(window).trigger('resize.touchui.tabbar');
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/source/js/dom/move/terminal.js:
--------------------------------------------------------------------------------
1 | TouchUI.prototype.DOM.move.terminal = {
2 |
3 | init: function() {
4 |
5 | // Add version number placeholder
6 | $(' ').prependTo("#terminal-output");
7 |
8 | // Create iScroll container for terminal
9 | var container = $('
').insertBefore("#terminal-output");
10 | var inner = $('
').appendTo(container);
11 | $("#terminal-output").appendTo(inner);
12 | $("#terminal-output-lowfi").appendTo(inner);
13 |
14 | }
15 |
16 | };
17 |
--------------------------------------------------------------------------------
/source/js/dom/overwrite/modal.js:
--------------------------------------------------------------------------------
1 | TouchUI.prototype.DOM.overwrite.modal = function() {
2 |
3 | //We need a reliable event for catching new modals for attaching a scrolling bar
4 | $.fn.modalBup = $.fn.modal;
5 | $.fn.modal = function(options, args) {
6 | // Update any other modifications made by others (i.e. OctoPrint itself)
7 | $.fn.modalBup.defaults = $.fn.modal.defaults;
8 |
9 | // Create modal, store into variable so we can trigger an event first before return
10 | var tmp = $(this).modalBup(options, args);
11 | if (options !== "hide") {
12 | $(this).trigger("modal.touchui", this);
13 | }
14 | return tmp;
15 | };
16 | $.fn.modal.prototype = { constructor: $.fn.modal };
17 | $.fn.modal.Constructor = $.fn.modal;
18 | $.fn.modal.defaults = $.fn.modalBup.defaults;
19 |
20 | }
21 |
--------------------------------------------------------------------------------
/source/js/dom/overwrite/pnotify.js:
--------------------------------------------------------------------------------
1 | TouchUI.prototype.DOM.overwrite.pnotify = function() {
2 |
3 | if(!this.settings.hasTouch) {
4 | var tmp = PNotify.prototype.options.stack;
5 | tmp.context = $('#scroll .page-container');
6 | PNotify.prototype.options.stack = tmp;
7 | }
8 |
9 | }
10 |
--------------------------------------------------------------------------------
/source/js/dom/overwrite/tabbar.js:
--------------------------------------------------------------------------------
1 | TouchUI.prototype.DOM.overwrite.tabbar = function() {
2 |
3 | // Force the webcam tab to load the webcam feed that original is located on the controls tab
4 | $('#tabs [data-toggle=tab]').each(function(ind, elm) {
5 |
6 | var data_event = jQuery._data(elm, "events");
7 | if (data_event && data_event.show) {
8 |
9 | // Get the currently attached events to the toggle
10 | var events = $.extend([], data_event.show),
11 | $elm = $(elm);
12 |
13 | // Remove all previous set events and call them after manipulating a few things
14 | $elm.off("show").on("show", function(e) {
15 | var scope = this,
16 | current = e.target.hash,
17 | previous = e.relatedTarget.hash;
18 |
19 | current = (current === "#control") ? "#control_without_webcam" : current;
20 | current = (current === "#webcam") ? "#control" : current;
21 |
22 | previous = (previous === "#control") ? "#control_without_webcam" : previous;
23 | previous = (previous === "#webcam") ? "#control" : previous;
24 |
25 | // Call previous unset functions (e.g. let's trigger the event onTabChange in all the viewModels)
26 | $.each(events, function(key, event) {
27 | event.handler.call(scope, {
28 | target: {
29 | hash: current
30 | },
31 | relatedTarget: {
32 | hash: previous
33 | }
34 | });
35 | });
36 | });
37 | }
38 |
39 | });
40 |
41 | }
42 |
--------------------------------------------------------------------------------
/source/js/dom/overwrite/tabdrop.js:
--------------------------------------------------------------------------------
1 | TouchUI.prototype.DOM.overwrite.tabdrop = function() {
2 | $.fn.tabdrop = function() {};
3 | $.fn.tabdrop.prototype = { constructor: $.fn.tabdrop };
4 | $.fn.tabdrop.Constructor = $.fn.tabdrop;
5 | }
6 |
--------------------------------------------------------------------------------
/source/js/dom/storage.js:
--------------------------------------------------------------------------------
1 | // Since I messed up by releasing start_kweb3.xinit without disabling private
2 | // mode, we now need to check if we can store anything at all in localstorage
3 | // the missing -P will prevent any localstorage
4 | if (TouchUI.prototype.settings.hasLocalStorage) {
5 | try {
6 | localStorage["TouchUIcanWeHazStorage"] = "true";
7 | TouchUI.prototype.DOM.storage = TouchUI.prototype.DOM.localstorage;
8 | delete localStorage["TouchUIcanWeHazStorage"];
9 | } catch(err) {
10 |
11 | // TODO: remove this is future
12 | if(TouchUI.prototype.settings.isEpiphanyOrKweb) {
13 | $(function() {
14 | new PNotify({
15 | type: 'error',
16 | title: "Private Mode detection:",
17 | text: "Edit the startup file 'start_kweb3.xinit' in '~/OctoPrint-TouchUI-autostart/' "+
18 | "and add the parameter 'P' after the dash. \n\n" +
19 | "For more information see the v0.3.3 release notes.",
20 | hide: false
21 | });
22 | });
23 | }
24 |
25 | console.info("Localstorage defined but failback to cookies due to errors.");
26 | TouchUI.prototype.DOM.storage = TouchUI.prototype.DOM.cookies;
27 | }
28 | } else {
29 | TouchUI.prototype.DOM.storage = TouchUI.prototype.DOM.cookies;
30 | }
31 |
32 | TouchUI.prototype.DOM.storage.migration = (TouchUI.prototype.DOM.storage === TouchUI.prototype.DOM.localstorage) ? function migration() {
33 |
34 | if (this.settings.hasLocalStorage) {
35 | if (document.cookie.indexOf("TouchUI.") !== -1) {
36 | console.info("TouchUI cookies migration.");
37 |
38 | var name = "TouchUI.";
39 | var ca = document.cookie.split(';');
40 | for (var i=0; i .dropdown-menu").length > 0) {
35 | $(document).trigger("click");
36 | }
37 | });
38 |
39 | // Redo scroll-to-end interface
40 | $("#term .terminal small.pull-right").html(' ').on("click", function() {
41 | viewModels.terminalViewModel.scrollToEnd();
42 | return false;
43 | });
44 |
45 | // Resize height of low-fi terminal to enable scrolling
46 | if ($("#terminal-output-lowfi").prop("scrollHeight")) {
47 | viewModels.terminalViewModel.plainLogOutput.subscribe(function() {
48 | $("#terminal-output-lowfi").height($("#terminal-output-lowfi").prop("scrollHeight"));
49 | });
50 | }
51 |
52 | // Overwrite terminal knockout functions (i.e. scroll to end)
53 | this.scroll.overwrite.call(this, viewModels.terminalViewModel);
54 |
55 | // Setup version tracking in terminal
56 | this.core.version.init.call(this, viewModels.softwareUpdateViewModel);
57 |
58 | // (Re-)Apply bindings to the new webcam div
59 | if ($("#webcam").length) {
60 | ko.applyBindings(viewModels.controlViewModel, $("#webcam")[0]);
61 | }
62 |
63 | // (Re-)Apply bindings to the new navigation div
64 | if ($("#navbar_login").length) {
65 | try {
66 | ko.applyBindings(viewModels.navigationViewModel, $("#navbar_login")[0]);
67 | } catch(err) {}
68 |
69 | viewModels.navigationViewModel.loginState.loggedIn.subscribe(function() {
70 | // Refresh scroll view when login state changed
71 | if( !self.settings.hasTouch ) {
72 | setTimeout(function() {
73 | self.scroll.currentActive.refresh();
74 | }, 0);
75 | }
76 | });
77 | }
78 |
79 | // (Re-)Apply bindings to the new system commands div
80 | if ($("#navbar_systemmenu").length) {
81 | ko.applyBindings(viewModels.navigationViewModel, $("#navbar_systemmenu")[0]);
82 | ko.applyBindings(viewModels.navigationViewModel, $("#divider_systemmenu")[0]);
83 | }
84 |
85 | // Force knockout to read the change
86 | $('.colorPicker').tinycolorpicker().on("change", function(e, hex, rgb, isTriggered) {
87 | if(isTriggered !== false) {
88 | $(this).find("input").trigger("change", [hex, rgb, false]);
89 | }
90 | });
91 |
92 | // Reuse for code below
93 | var refreshUrl = function(href) {
94 | return href.split("?")[0] + "?ts=" + new Date().getTime();
95 | }
96 |
97 | // Reload CSS if needed
98 | self.settings.refreshCSS.subscribe(function(hasRefresh) {
99 | if (hasRefresh || hasRefresh === "fast") {
100 | // Wait 2 seconds, so we're not too early
101 | setTimeout(function() {
102 | var $css = $("#touchui-css");
103 | $css.attr("href", refreshUrl($css.attr("href")));
104 | self.settings.refreshCSS(false);
105 | }, (hasRefresh === "fast") ? 0 : 1200);
106 | }
107 | });
108 |
109 | // Reload CSS or LESS after saving our settings
110 | self.settings.hasCustom.subscribe(function(customCSS) {
111 | if(customCSS !== "") {
112 | var $css = $("#touchui-css");
113 | var href = $css.attr("href");
114 |
115 | if(customCSS) {
116 | href = href.replace("touchui.css", "touchui.custom.css");
117 | } else {
118 | href = href.replace("touchui.custom.css", "touchui.css");
119 | }
120 |
121 | $css.attr("href", refreshUrl(href));
122 | }
123 | });
124 |
125 | // Check if we need to update an old LESS file with a new LESS one
126 | var requireNewCSS = ko.computed(function() {
127 | return self.settings.requireNewCSS() && viewModels.loginStateViewModel.isAdmin();
128 | });
129 | requireNewCSS.subscribe(function(requireNewCSS) {
130 | if(requireNewCSS) {
131 | setTimeout(function() {
132 | self.core.less.save.call(self, self);
133 | }, 100);
134 | }
135 | });
136 |
137 | // Evuluate computed subscriber defined above:
138 | // In OctoPrint >1.3.5 the settings will be defined upfront
139 | requireNewCSS.notifySubscribers(self.settings.requireNewCSS() && viewModels.loginStateViewModel.isAdmin());
140 |
141 | //TODO: move this
142 | $("li.dropdown#navbar_login > a.dropdown-toggle").off("click").on("click", function(e) {
143 | e.stopImmediatePropagation();
144 | e.preventDefault();
145 |
146 | $(this).parent().toggleClass("open");
147 | });
148 |
149 | if (window.top.postMessage) {
150 | // Tell bootloader we're ready with giving him the expected version for the bootloader
151 | // if version is lower on the bootloader, then the bootloader will throw an update msg
152 | window.top.postMessage(self.settings.requiredBootloaderVersion, "*");
153 |
154 | // Sync customization with bootloader
155 | window.top.postMessage([true, self.settings.colors.mainColor(), self.settings.colors.bgColor()], "*");
156 |
157 | ko.computed(function() {
158 | window.top.postMessage([true, self.settings.colors.mainColor(), self.settings.colors.bgColor()], "*");
159 | });
160 |
161 | // Stop watching for errors
162 | $(window).off("error.touchui");
163 |
164 | // Trigger wake-up for iScroll
165 | if(window.dispatchEvent) {
166 | window.dispatchEvent(new Event('resize'));
167 | }
168 | }
169 |
170 | // Re-render tabbar
171 | $(window).trigger('resize.touchui.tabbar');
172 |
173 | // We will win the DOM manipulation war!
174 | setTimeout(function() {
175 | self.plugins.multiWebCam.call(self);
176 | }, 0);
177 |
178 | // Disable clicking outside models
179 | if (viewModels.settingsViewModel.appearance_closeModalsWithClick) {
180 | $('#settings-appearanceCloseModalsWithClick').parent().addClass('touchui_disabled');
181 | $('(Disabled and managed by TouchUI) ').appendTo($('#settings-appearanceCloseModalsWithClick').parent());
182 |
183 | this.settings.closeDialogsOutside.subscribe(function(close) {
184 | viewModels.settingsViewModel.appearance_closeModalsWithClick(close);
185 | });
186 |
187 | this.settings.closeDialogsOutside.valueHasMutated();
188 | }
189 | }
190 | }
191 |
--------------------------------------------------------------------------------
/source/js/knockout/viewModel.js:
--------------------------------------------------------------------------------
1 | TouchUI.prototype.knockout.viewModel = function() {
2 | var self = this;
3 |
4 | // Subscribe to OctoPrint events
5 | self.onStartupComplete = function () {
6 | if (self.isActive()) {
7 | self.DOM.overwrite.tabbar.call(self);
8 | }
9 | self.knockout.isReady.call(self, self.core.bridge.allViewModels);
10 | if (self.isActive()) {
11 | self.plugins.init.call(self, self.core.bridge.allViewModels);
12 | }
13 | }
14 |
15 | self.onBeforeBinding = function() {
16 | ko.mapping.fromJS(self.core.bridge.allViewModels.settingsViewModel.settings.plugins.touchui, {}, self.settings);
17 | }
18 |
19 | self.onSettingsBeforeSave = function() {
20 | self.core.less.save.call(self);
21 | }
22 |
23 | self.onTabChange = function() {
24 | if (self.isActive()) {
25 | self.animate.hide.call(self, "navbar");
26 |
27 | if(!self.settings.hasTouch && self.scroll.currentActive) {
28 | self.scroll.currentActive.refresh();
29 | setTimeout(function() {
30 | self.scroll.currentActive.refresh();
31 | }, 0);
32 | }
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/source/js/plugins/_init.js:
--------------------------------------------------------------------------------
1 | TouchUI.prototype.plugins.init = function (viewModels) {
2 | this.plugins.screenSquish(viewModels.pluginManagerViewModel);
3 | }
4 |
--------------------------------------------------------------------------------
/source/js/plugins/disable.js:
--------------------------------------------------------------------------------
1 | TouchUI.prototype.plugins.disable = {
2 | plugins: [
3 | {
4 | htmlId: '#settings_plugin_themeify',
5 | name: 'Themeify'
6 | }, {
7 | functionName: 'TempsgraphViewModel',
8 | name: 'TempsGraph'
9 | }, {
10 | functionName: 'WebcamTabViewModel',
11 | name: 'WebcamTab',
12 | extra: function() {
13 | $('#tab_plugin_webcamtab_link').remove();
14 | }
15 | }, {
16 | functionName: 'AblExpertViewModel',
17 | name: 'ABLExpert',
18 | extra: function() {
19 | $('#settings_plugin_ABL_Expert').hide();
20 | $('#settings_plugin_ABL_Expert_link').hide();
21 | $('#processing_dialog_plugin_ABL_Expert').hide();
22 | $('#results_dialog_plugin_ABL_Expert').hide();
23 | }
24 | }
25 | ],
26 |
27 | init: function () {
28 | var self = this;
29 |
30 | _.remove(OCTOPRINT_VIEWMODELS, function(viewModel) {
31 | return _.some(
32 | self.plugins.disable.plugins,
33 | self.plugins.disable.find.bind(
34 | _.flattenDeep(
35 | _.isPlainObject(viewModel) ? _.values(viewModel) : viewModel
36 | )
37 | )
38 | );
39 | });
40 | },
41 |
42 | find: function(plugin) {
43 | var result = false;
44 |
45 | if (plugin.htmlId) {
46 | result = this.indexOf(plugin.htmlId) !== -1;
47 | }
48 |
49 | if (plugin.functionName) {
50 | result = _.some(this, function(viewModelProp) {
51 | return viewModelProp.name && viewModelProp.name === plugin.functionName;
52 | });
53 | }
54 |
55 | if (result) {
56 | console.info("TouchUI: " + plugin.name + " is disabled while TouchUI is active.");
57 |
58 | if (plugin.extra) {
59 | plugin.extra();
60 | }
61 | }
62 |
63 | return result;
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/source/js/plugins/multiwebcam.js:
--------------------------------------------------------------------------------
1 | TouchUI.prototype.plugins.multiWebCam = function() {
2 |
3 | // Manually move multiWebCam (hard move)
4 | if( $("#camControl").length) {
5 | $("#camControl").appendTo('#webcam');
6 | }
7 |
8 | }
9 |
--------------------------------------------------------------------------------
/source/js/plugins/psucontrol.js:
--------------------------------------------------------------------------------
1 | TouchUI.prototype.plugins.psuControl = function() {
2 |
3 | // Manually move navbar temp (hard move)
4 | if( $("#navbar_plugin_psucontrol a").length > 0 ) {
5 | $("#navbar_plugin_psucontrol a")
6 | .text('PSU Control');
7 | }
8 |
9 | }
10 |
--------------------------------------------------------------------------------
/source/js/plugins/screensquish.js:
--------------------------------------------------------------------------------
1 | TouchUI.prototype.plugins.screenSquish = function(pluginManagerViewModel) {
2 | var shown = false;
3 |
4 | pluginManagerViewModel.plugins.items.subscribe(function() {
5 |
6 | var ScreenSquish = pluginManagerViewModel.plugins.getItem(function(elm) {
7 | return (elm.key === "ScreenSquish");
8 | }, true) || false;
9 |
10 | if(!shown && ScreenSquish && ScreenSquish.enabled) {
11 | shown = true;
12 | new PNotify({
13 | title: 'TouchUI: ScreenSquish is running',
14 | text: 'Running ScreenSquish and TouchUI will give issues since both plugins try the same, we recommend turning off ScreenSquish.',
15 | icon: 'glyphicon glyphicon-question-sign',
16 | type: 'error',
17 | hide: false,
18 | confirm: {
19 | confirm: true,
20 | buttons: [{
21 | text: 'Disable ScreenSquish',
22 | addClass: 'btn-primary',
23 | click: function(notice) {
24 | if(!ScreenSquish.pending_disable) {
25 | pluginManagerViewModel.togglePlugin(ScreenSquish);
26 | }
27 | notice.remove();
28 | }
29 | }]
30 | },
31 | });
32 | }
33 |
34 | });
35 |
36 | };
37 |
--------------------------------------------------------------------------------
/source/js/scroll/_beforeLoad.js:
--------------------------------------------------------------------------------
1 | TouchUI.prototype.scroll.beforeLoad = function() {
2 |
3 | // Manipulate DOM for iScroll before knockout binding kicks in
4 | if (!this.settings.hasTouch) {
5 | $('
').insertBefore('.page-container');
6 | $('.page-container').appendTo("#scroll");
7 | }
8 |
9 | }
10 |
--------------------------------------------------------------------------------
/source/js/scroll/_init.js:
--------------------------------------------------------------------------------
1 | TouchUI.prototype.scroll.init = function() {
2 | var self = this;
3 |
4 | if ( this.settings.hasTouch ) {
5 | var width = $(window).width();
6 |
7 | // Covert VH to the initial height (prevent height from jumping when navigation bar hides/shows)
8 | $("#temperature-graph").parent().height($("#temperature-graph").parent().outerHeight());
9 | $("#terminal-scroll").height($("#terminal-scroll").outerHeight());
10 | $("#terminal-sendpanel").css("top", $("#terminal-scroll").outerHeight()-1);
11 |
12 | $(window).on("resize", function() {
13 |
14 | if(width !== $(window).width()) {
15 | $("#temperature-graph").parent().height($("#temperature-graph").parent().outerHeight());
16 | $("#terminal-scroll").css("height", "").height($("#terminal-scroll").outerHeight());
17 | $("#terminal-sendpanel").css("top", $("#terminal-scroll").outerHeight()-1);
18 | width = $(window).width();
19 | }
20 |
21 |
22 | });
23 |
24 | } else {
25 |
26 | // Set overflow hidden for best performance
27 | $("html").addClass("emulateTouch");
28 |
29 | self.scroll.terminal.init.call(self);
30 | self.scroll.body.init.call(self);
31 | self.scroll.modal.init.call(self);
32 | self.scroll.overlay.init.call(self);
33 |
34 | $(document).on("slideCompleted", function() {
35 | self.scroll.currentActive.refresh();
36 | });
37 |
38 | // Refresh body on dropdown click
39 | $(document).on("click", ".pagination ul li a", function() {
40 | setTimeout(function() {
41 | self.scroll.currentActive.refresh();
42 | }, 0);
43 | });
44 |
45 | }
46 |
47 | }
48 |
--------------------------------------------------------------------------------
/source/js/scroll/block-events.js:
--------------------------------------------------------------------------------
1 | TouchUI.prototype.scroll.blockEvents = {
2 | className: "no-pointer",
3 |
4 | scrollStart: function($elm, iScrollInstance) {
5 | $elm.addClass(this.className);
6 | },
7 |
8 | scrollEnd: function($elm, iScrollInstance) {
9 | $elm.removeClass(this.className);
10 | iScrollInstance.refresh();
11 | }
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/source/js/scroll/body.js:
--------------------------------------------------------------------------------
1 | TouchUI.prototype.scroll.body = {
2 |
3 | init: function() {
4 | var self = this;
5 | var scrollStart = false;
6 | var $noPointer = $('.page-container');
7 |
8 | // Create main body scroll
9 | self.scroll.iScrolls.body = new IScroll("#scroll", self.scroll.defaults.iScroll);
10 | self.scroll.currentActive = self.scroll.iScrolls.body;
11 |
12 | // Block everthing while scrolling
13 | var scrollStart = self.scroll.blockEvents.scrollStart.bind(self.scroll.blockEvents, $noPointer, self.scroll.iScrolls.body),
14 | scrollEnd = self.scroll.blockEvents.scrollEnd.bind(self.scroll.blockEvents, $noPointer, self.scroll.iScrolls.body);
15 |
16 | // Disable all JS events while scrolling for best performance
17 | self.scroll.iScrolls.body.on("scrollStart", scrollStart);
18 | self.scroll.iScrolls.body.on("onBeforeScrollStart", scrollStart);
19 | self.scroll.iScrolls.body.on("scrollEnd", scrollEnd);
20 | self.scroll.iScrolls.body.on("scrollCancel", scrollEnd);
21 |
22 | // Prevent any misfortune
23 | $(document).on("mouseup.prevent.pointer touchend.prevent.pointer", function() {
24 | $noPointer.removeClass('no-pointer');
25 | });
26 |
27 | }
28 |
29 | }
30 |
--------------------------------------------------------------------------------
/source/js/scroll/modal.js:
--------------------------------------------------------------------------------
1 | TouchUI.prototype.scroll.modal = {
2 | stack: [],
3 | dropdown: null,
4 |
5 | init: function() {
6 | var $document = $(document),
7 | self = this;
8 |
9 | $document.on("modal.touchui", function(e, elm) {
10 | var $modalElm = $(elm),
11 | $modalContainer = $(elm).parent();
12 |
13 | // Create temp iScroll within the modal
14 | var curModal = new IScroll($modalContainer[0], self.scroll.defaults.iScroll);
15 |
16 | // Store into stack
17 | self.scroll.modal.stack.push(curModal);
18 | self.scroll.currentActive = curModal;
19 |
20 | // Force iScroll to get the correct scrollHeight
21 | setTimeout(function() {
22 | if(curModal) {
23 | curModal.refresh();
24 | }
25 | }, 0);
26 | // And Refresh again after animation
27 | setTimeout(function() {
28 | if(curModal) {
29 | curModal.refresh();
30 | }
31 | }, 800);
32 |
33 | // Store bindings into variable for future reference
34 | var scrollStart = self.scroll.blockEvents.scrollStart.bind(self.scroll.blockEvents, $modalElm, curModal),
35 | scrollEnd = self.scroll.blockEvents.scrollEnd.bind(self.scroll.blockEvents, $modalElm, curModal);
36 |
37 | // Disable all JS events while scrolling for best performance
38 | curModal.on("scrollStart", scrollStart);
39 | curModal.on("scrollEnd", scrollEnd);
40 | curModal.on("scrollCancel", scrollEnd);
41 |
42 | // Refresh the scrollHeight and scroll back to top with these actions:
43 | $document.on("click.scrollHeightTouchUI", '[data-toggle="tab"], .pagination ul li a', function(e) {
44 | curModal._end(e);
45 |
46 | setTimeout(function() {
47 | curModal.refresh();
48 | curModal.scrollTo(0, 0);
49 | }, 0);
50 | });
51 |
52 | // Kill it with fire!
53 | $modalElm.one("destroy", function() {
54 | $document.off("click.scrollHeightTouchUI");
55 | self.scroll.modal.stack.pop();
56 |
57 | if(self.scroll.modal.stack.length > 0) {
58 | self.scroll.currentActive = self.scroll.modal.stack[self.scroll.modal.stack.length-1];
59 | } else {
60 | self.scroll.currentActive = self.scroll.iScrolls.body;
61 | }
62 |
63 | curModal.destroy();
64 | curModal.off("scrollStart", scrollStart);
65 | curModal.off("scrollEnd", scrollEnd);
66 | curModal.off("scrollCancel", scrollEnd);
67 | curModal = undefined;
68 | });
69 |
70 | });
71 |
72 | // Triggered when we create the dropdown and need scrolling
73 | $document.on("dropdown-open.touchui", function(e, elm) {
74 | var $elm = $(elm);
75 |
76 | // Create dropdown scroll
77 | self.scroll.modal.dropdown = new IScroll(elm, {
78 | scrollbars: true,
79 | mouseWheel: true,
80 | interactiveScrollbars: true,
81 | shrinkScrollbars: "scale"
82 | });
83 |
84 | // Set scroll to active item
85 | self.scroll.modal.dropdown.scrollToElement($elm.find('li.active')[0], 0, 0, -30);
86 |
87 | // Disable scrolling in active modal
88 | self.scroll.modal.stack[self.scroll.modal.stack.length-1].disable();
89 |
90 | // Store bindings into variable for future reference
91 | var scrollStart = self.scroll.blockEvents.scrollStart.bind(self.scroll.blockEvents, $elm, self.scroll.modal.dropdown),
92 | scrollEnd = self.scroll.blockEvents.scrollEnd.bind(self.scroll.blockEvents, $elm, self.scroll.modal.dropdown);
93 |
94 | // Disable all JS events for smooth scrolling
95 | self.scroll.modal.dropdown.on("scrollStart", scrollStart);
96 | self.scroll.modal.dropdown.on("scrollEnd", scrollEnd);
97 | self.scroll.modal.dropdown.on("scrollCancel", scrollEnd);
98 |
99 | $document.on("dropdown-closed.touchui", function() {
100 | // Enable active modal
101 | self.scroll.modal.stack[self.scroll.modal.stack.length-1].enable();
102 |
103 | self.scroll.modal.dropdown.off("scrollStart", scrollStart);
104 | self.scroll.modal.dropdown.off("scrollEnd", scrollEnd);
105 | self.scroll.modal.dropdown.off("scrollCancel", scrollEnd);
106 | });
107 |
108 | });
109 |
110 | }
111 | }
112 |
--------------------------------------------------------------------------------
/source/js/scroll/overlay.js:
--------------------------------------------------------------------------------
1 | TouchUI.prototype.scroll.overlay = {
2 |
3 | mainItems: ['#offline_overlay', '#reloadui_overlay'],
4 | init: function() {
5 | var self = this;
6 |
7 | self.scroll.iScrolls.overlay = [];
8 |
9 | var $items = $(this.scroll.overlay.mainItems);
10 | $items.each(function(ind, elm) {
11 | var child = $(elm).children("#" + $(elm).attr("id") + "_wrapper");
12 | var div = $('
').prependTo(elm);
13 | child.appendTo(div);
14 |
15 | $(elm).addClass("iscroll");
16 |
17 | self.scroll.iScrolls.overlay[ind] = new IScroll(elm, self.scroll.defaults.iScroll);
18 | });
19 |
20 | },
21 |
22 | refresh: function() {
23 | var self = this;
24 |
25 | setTimeout(function() {
26 | $.each(self.scroll.iScrolls.overlay, function(ind) {
27 | self.scroll.iScrolls.overlay[ind].refresh();
28 | });
29 | }, 0);
30 |
31 | }
32 |
33 | }
34 |
--------------------------------------------------------------------------------
/source/js/scroll/overwrite.js:
--------------------------------------------------------------------------------
1 | TouchUI.prototype.scroll.overwrite = function(terminalViewModel) {
2 | var self = this;
3 |
4 | if ( !this.settings.hasTouch ) {
5 |
6 | // Enforce no scroll jumping
7 | $("#scroll").on("scroll", function() {
8 | if($("#scroll").scrollTop() !== 0) {
9 | $("#scroll").scrollTop(0);
10 | }
11 | });
12 |
13 | // Refresh terminal scroll height
14 | terminalViewModel.displayedLines.subscribe(function() {
15 | self.scroll.iScrolls.terminal.refresh();
16 | });
17 |
18 | // Overwrite scrollToEnd function with iScroll functions
19 | terminalViewModel.scrollToEnd = function() {
20 | self.scroll.iScrolls.terminal.refresh();
21 | self.scroll.iScrolls.terminal.scrollTo(0, self.scroll.iScrolls.terminal.maxScrollY);
22 | };
23 |
24 | // Overwrite orginal helper, add one step and call the orginal function
25 | var showOfflineOverlay = window.showOfflineOverlay;
26 | window.showOfflineOverlay = function(title, message, reconnectCallback) {
27 | showOfflineOverlay.call(this, title, message, reconnectCallback);
28 | self.scroll.overlay.refresh.call(self);
29 | };
30 |
31 | // Overwrite orginal helper, add one step and call the orginal function
32 | var showConfirmationDialog = window.showConfirmationDialog;
33 | window.showConfirmationDialog = function(message, onacknowledge) {
34 | self.scroll.iScrolls.body.scrollTo(0, 0, 500);
35 | showConfirmationDialog.call(this, message, onacknowledge);
36 | };
37 |
38 | // Overwrite orginal helper, add one step and call the orginal function
39 | var showReloadOverlay = $.fn.show;
40 | $.fn.show = function(e,r,i) {
41 | if($(this).hasClass("iscroll")) {
42 | setTimeout(function() {
43 | self.scroll.overlay.refresh.call(self);
44 | }, 0);
45 | }
46 |
47 | return showReloadOverlay.call(this,e,r,i);
48 | }
49 |
50 | } else {
51 |
52 | // Overwrite scrollToEnd function with #terminal-scroll as scroller
53 | terminalViewModel.scrollToEnd = function() {
54 | var $container = $("#terminal-scroll");
55 | if ($container.length) {
56 | $container.scrollTop($container[0].scrollHeight - $container.height())
57 | }
58 | }
59 |
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/source/js/scroll/terminal.js:
--------------------------------------------------------------------------------
1 | TouchUI.prototype.scroll.terminal = {
2 |
3 | init: function() {
4 | var self = this;
5 |
6 | // Create scrolling for terminal
7 | self.scroll.iScrolls.terminal = new IScroll("#terminal-scroll", self.scroll.defaults.iScroll);
8 |
9 | // Enforce the right scrollheight and disable main scrolling if we have a scrolling content
10 | self.scroll.iScrolls.terminal.on("beforeScrollStart", function() {
11 | self.scroll.iScrolls.terminal.refresh();
12 |
13 | if(this.hasVerticalScroll) {
14 | self.scroll.iScrolls.body.disable();
15 | }
16 | });
17 | self.scroll.iScrolls.terminal.on("scrollEnd", function() {
18 | self.scroll.iScrolls.body.enable();
19 | });
20 |
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/source/less/_variables.less:
--------------------------------------------------------------------------------
1 | @touchui-base-path: "../";
2 | @fontawesome-path: "../fonts/fontawesome/";
3 | @sourcecodepro-path: "../fonts/sourcecodepro/";
4 | @touchui-path: "../fonts/touchui/";
5 |
6 | /* General */
7 | @main-font-size: 16;
8 | @main-color: #00B0FF;
9 | @main-color-text: contrast(@main-color, @dark-color, @light-color, 51%);
10 |
11 | @main-color-dark: darken(@main-color, 5%);
12 | @main-color-dark-text: contrast(@main-color-dark, @dark-color, @light-color, 60%);
13 |
14 | @main-color-darker: darken(@main-color, 15%);
15 |
16 | @main-background: @dark-color;
17 | @text-color: @light-color;
18 | @link-color: @main-color;
19 |
20 | /* Grid properties */
21 | @main-gutter: 30px;
22 | @modal-gutter: 30px;
23 | @terminal-gutter: 10px;
24 | @dropdown-gutter: 15px;
25 |
26 | /* Tables, fileList, progress */
27 | @table-row-background: mix(@main-background, contrast(@main-background), 80%);
28 | @table-row-header-background: @main-color;
29 | @table-row-header-text-color: @main-color-text;
30 |
31 | /* Progress */
32 | @progress-background: @table-row-background;
33 | @progress-active-background: @main-color;
34 |
35 | /* fileList */
36 | @swipe-list-background-color: @table-row-background;
37 | @swipe-list-background-open-color: mix(@main-color, @dark-color, 60%);
38 |
39 | /* Modal */
40 | @modal-background: @light-color;
41 | @modal-backdrop: @dark-color;
42 | @modal-footer-background: fade(contrast(@modal-background), 5%);
43 | @modal-border: fade(contrast(@modal-background), 7%);
44 | @modal-text: @dark-color;
45 |
46 | /* Buttons */
47 | @btn-background: @light-dark-color;
48 | @btn-text: contrast(@light-dark-color, @dark-color, @light-color);
49 |
50 | @btn-main-background: @main-color;
51 | @btn-main-text: @main-color-text;
52 |
53 | @btn-danger-background: #D40000;
54 | @btn-danger-text: contrast(@btn-danger-background, @dark-color, @light-color);
55 |
56 | /* Dropdown + indication */
57 | @online-color: #9AFD00;
58 | @offline-color: red;
59 | @dropdown-color: @main-color;
60 | @dropdown-color-text: @main-color-text;
61 | @dropdown-divider-color: @main-color-dark;
62 |
63 | /* Termional */
64 | @terminal-color: #0F0;
65 | @terminal-background: @main-background;
66 | @terminal-text-color: @terminal-color;
67 |
68 | /* iScroll Styling */
69 | @terminal-iScroll-color: fade(@terminal-color, 70%);
70 | @dropdown-iScroll-color: fade(@light-darker-dark-color, 70%);
71 | @main-iScroll-color: fade(contrast(@main-background), 70%);
72 |
73 | /* Inputs */
74 | @input-background: contrast(@main-background, @dark-light-color, @light-dark-color, 50%);
75 | @input-text-color: contrast(@input-background);
76 |
77 | @addon-background: @dark-lighter-color;
78 | @addon-text-color: @light-color;
79 |
80 | /* keyboard */
81 | @keyboard-background: @dark-light-color;
82 | @keyboard-color: contrast(@keyboard-background);
83 | @keyboard-accept: #00C751;
84 | @keyboard-remove: #9E0000;
85 |
86 | /* More General styles used for contrast etc. */
87 | @dark-color: black;
88 | @dark-light-color: lighten(@dark-color, 20%);
89 | @dark-lighter-color: lighten(@dark-color, 30%);
90 |
91 | @neutral-color: #555;
92 |
93 | @light-color: white;
94 | @light-dark-color: darken(@light-color, 2%);
95 | @light-darker-color: darken(@light-color, 20%);
96 | @light-darker-dark-color: darken(@light-color, 50%);
97 | @light-darker-darker-color: darken(@light-color, 55%);
98 |
--------------------------------------------------------------------------------
/source/less/components/dropdown.less:
--------------------------------------------------------------------------------
1 | .dropdown {
2 | &-toggle {
3 | &.open {
4 | + .dropdown-menu, .dropdown-menu {
5 | display: block;
6 | }
7 | }
8 | }
9 |
10 | &.open {
11 | overflow: visible !important;
12 |
13 | .dropdown-menu {
14 | background-color: @dropdown-color !important;
15 |
16 | a {
17 | text-indent: 0 !important;
18 | }
19 | }
20 | }
21 | }
22 |
23 | .dropdown-submenu {
24 | > a {
25 | &::after {
26 | .font-icon-reset();
27 | content: @fa-legacy-var-caret-down;
28 | float: none;
29 | position: absolute;
30 | top: 13px;
31 | margin: 0;
32 | right: 30px;
33 | border: 0 none !important;
34 | }
35 | }
36 | &:hover {
37 | >.dropdown-menu {
38 | display: none;
39 | }
40 | }
41 | &.open {
42 | .dropdown-menu {
43 | display: block !important;
44 | }
45 | > a {
46 | &:after {
47 | content: @fa-legacy-var-caret-up !important;
48 | }
49 | }
50 | }
51 | }
52 |
53 | .dropdown-menu {
54 | .border-radius(0);
55 | .transform(translateZ(0));
56 |
57 | background: @dropdown-color;
58 | color: @dropdown-color-text;
59 | border: 1px solid @dropdown-divider-color !important;
60 |
61 | margin-top: 10px;
62 | padding: (@dropdown-gutter / 3) 0;
63 | max-width: 100vw;
64 |
65 | @media (max-width: 300px) {
66 | width: 90vw;
67 | min-width: 0 !important;
68 | }
69 |
70 | &:after {
71 | display: none;
72 | }
73 |
74 | li {
75 | p {
76 | .rem(font-size, 14);
77 | .rem(line-height, 25);
78 |
79 | float: none;
80 | color: inherit;
81 | margin: 0;
82 | padding: 0 @dropdown-gutter;
83 | }
84 |
85 | a {
86 | color: @dropdown-color-text;
87 | .rem(font-size, 15) !important;
88 | width: 100%;
89 | .box-sizing(border-box);
90 |
91 | padding: 0 @dropdown-gutter;
92 | .rem(line-height, 40);
93 |
94 | i {
95 | color: inherit;
96 | }
97 |
98 | &:hover, &:focus {
99 | color: @dropdown-color-text;
100 | background: @dropdown-color;
101 | }
102 | &:active {
103 | background: darken(@dropdown-color, 15%);
104 | }
105 | }
106 | }
107 |
108 | .dropdown-menu {
109 | position: static;
110 | z-index: 0;
111 | margin: 0;
112 | float: none;
113 | border: 0 none !important;
114 | padding: 0;
115 |
116 | &:before {
117 | display: none;
118 | }
119 |
120 | @media (max-width: 300px) {
121 | width: auto;
122 | }
123 | }
124 |
125 | &:before {
126 | right: 6vw;
127 | top: -20px;
128 | border-left-width: 10px;
129 | border-right-width: 10px;
130 | border-bottom-width: 10px;
131 | border-bottom-color: @dropdown-color !important;
132 | }
133 |
134 | .btn {
135 | background: darken(@btn-main-background, 5%);
136 |
137 | &:active {
138 | background: darken(@btn-main-background, 15%);
139 | }
140 | }
141 |
142 | .divider {
143 | margin: (@dropdown-gutter / 3) 0;
144 | background-color: @dropdown-divider-color;
145 | border: 0 none;
146 | }
147 |
148 | &:before {
149 | content: "";
150 | border: 10px solid transparent;
151 | border-bottom-color: @dropdown-color;
152 | position: absolute;
153 | top: -18px;
154 | right: 43px;
155 | }
156 | }
157 |
--------------------------------------------------------------------------------
/source/less/components/emulate.touch.less:
--------------------------------------------------------------------------------
1 | &.emulateTouch {
2 | overflow: hidden;
3 | user-select: none;
4 |
5 | body {
6 | overflow: hidden;
7 |
8 | .modal-scrollable,
9 | .show-dropdown {
10 | overflow: hidden;
11 | }
12 |
13 | .page-container {
14 | height: auto;
15 | min-height: 100%;
16 | }
17 |
18 | #webcam_container {
19 | pointer-events: none;
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/source/less/components/notifications.less:
--------------------------------------------------------------------------------
1 | .ui-pnotify-container {
2 | .border-radius(0);
3 | }
4 | .ui-pnotify-closer {
5 | visibility: visible !important;
6 | padding: 5px 10px;
7 | .rem(font-size, 28);
8 |
9 | .glyphicon-remove:after {
10 | .font-icon-reset() !important;
11 | content: @fa-legacy-var-remove;
12 | }
13 | }
14 | .ui-pnotify-sticker {
15 | display: none;
16 | }
17 | .ui-pnotify-title {
18 | margin-bottom: 5px;
19 | }
20 | .ui-pnotify {
21 | position: absolute;
22 | left: 0;
23 | width: 100% !important;
24 | right: auto !important;
25 | }
26 |
--------------------------------------------------------------------------------
/source/less/components/overlays.less:
--------------------------------------------------------------------------------
1 | #drop_overlay {
2 | display: none !important;
3 | }
4 | #offline_overlay, #reloadui_overlay {
5 | position: fixed;
6 | top:0;
7 | bottom:0;
8 | overflow-y:auto;
9 | -webkit-overflow-scrolling: touch;
10 |
11 | #offline_overlay_background {
12 | .transform(translateZ(0));
13 | }
14 |
15 | #offline_overlay_wrapper, #reloadui_overlay_wrapper {
16 | padding-top: 20px;
17 | }
18 |
19 | .hero-unit {
20 | .rem(font-size, 18);
21 | .rem(line-height, 30);
22 | }
23 |
24 | .container {
25 | width: 100%;
26 |
27 | .hero-unit {
28 | background-color: @neutral-color;
29 | color: contrast(@neutral-color);
30 | padding: 20px;
31 | .border-radius(0);
32 |
33 | h1 {
34 | .rem(font-size, 32) !important;
35 | }
36 |
37 | h1, p {
38 | margin-bottom: 10px;
39 | }
40 | }
41 | }
42 |
43 | &.iscroll {
44 | top: 0;
45 | bottom: 0;
46 | left: 0;
47 | width: 100%;
48 | overflow: hidden;
49 | position: absolute;
50 |
51 | > div:first-child {
52 | position: absolute;
53 | z-index: 1;
54 | -webkit-tap-highlight-color: rgba(0,0,0,0);
55 | width: 100%;
56 |
57 | .transform(translateZ(0));
58 | .user-select(none);
59 | .text-adjust(none);
60 |
61 | > div {
62 | position: relative;
63 | top: auto;
64 | left: auto;
65 | right: auto;
66 | bottom: auto;
67 | }
68 | }
69 |
70 | .iScrollIndicator {
71 | background: @main-iScroll-color !important;
72 | border: 0 none !important;
73 | }
74 | }
75 |
76 | }
77 |
--------------------------------------------------------------------------------
/source/less/components/pagination.less:
--------------------------------------------------------------------------------
1 | .pagination {
2 | color: transparent;
3 |
4 | ul {
5 | > li {
6 |
7 | &.disabled,
8 | &.active {
9 | > a {
10 | background: transparent;
11 | color: @text-color;
12 | }
13 | }
14 | > a {
15 | padding: 10px 15px;
16 | .rem(font-size, 21);
17 | background: @main-color;
18 | color: @main-color-text;
19 |
20 | &:active {
21 | background: @main-color-darker;
22 | color: @main-color-dark-text;
23 | }
24 | }
25 |
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/source/less/components/popover.less:
--------------------------------------------------------------------------------
1 | .popover {
2 | background: @main-color;
3 | color: @main-color-text;
4 | border-color: @main-color-dark;
5 | padding: 0;
6 | border-radius: 0;
7 |
8 | &-title {
9 | background: @main-color-dark;
10 | border-bottom-color: @main-color-dark;
11 | color: @main-color-dark-text;
12 | font-weight: bold;
13 | border-radius: 0;
14 | }
15 |
16 | &-content {
17 | padding: 6px 0;
18 | }
19 |
20 | &.bottom .arrow:after {
21 | border-bottom-color: @main-color-dark;
22 | }
23 |
24 | &.top .arrow:after {
25 | border-top-color: @main-color-dark;
26 | }
27 |
28 | &.left .arrow:after {
29 | border-left-color: @main-color-dark;
30 | }
31 |
32 | &.right .arrow:after {
33 | border-right-color: @main-color-dark;
34 | }
35 | }
--------------------------------------------------------------------------------
/source/less/components/progress.less:
--------------------------------------------------------------------------------
1 | .progress {
2 | background: @progress-background;
3 |
4 | .bar {
5 | background: @progress-active-background;
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/source/less/components/scroll.less:
--------------------------------------------------------------------------------
1 | #scroll {
2 | position: absolute;
3 | z-index: 1;
4 | top: 0;
5 | bottom: 0;
6 | left: 0;
7 | width: 100%;
8 | overflow: hidden;
9 |
10 | .page-container,
11 | #terminal-scroll-inner,
12 | #terminal-scroll > pre { //TODO remove ' > pre' backwards compatibility for <1.3.0
13 | position: absolute;
14 | z-index: 1;
15 | -webkit-tap-highlight-color: rgba(0,0,0,0);
16 | width: 100%;
17 |
18 | .transform(translateZ(0));
19 | .user-select(none);
20 | .text-adjust(none);
21 | }
22 | .page-container {
23 | z-index: auto;
24 | }
25 | #terminal-scroll {
26 | position: relative;
27 | }
28 |
29 | .iScrollIndicator {
30 | background: @main-iScroll-color !important;
31 | border: 0 none !important;
32 | }
33 |
34 | }
35 |
--------------------------------------------------------------------------------
/source/less/components/scrollbars.less:
--------------------------------------------------------------------------------
1 | &.touch,
2 | &.touchevents {
3 | .show-dropdown,
4 | #terminal-scroll {
5 | overflow-y: scroll !important;
6 | -webkit-overflow-scrolling: auto;
7 | }
8 |
9 | ::-webkit-scrollbar {
10 | width: 2px;
11 | height: 2px;
12 | background: transparent;
13 | }
14 |
15 | ::-webkit-scrollbar-thumb {
16 | background: @main-color;
17 | }
18 |
19 | body {
20 | .modal-scrollable {
21 | position: fixed !important;
22 | }
23 | }
24 |
25 | }
26 |
--------------------------------------------------------------------------------
/source/less/components/tinycolorpicker.less:
--------------------------------------------------------------------------------
1 | .colorPickers {
2 | .flex-display();
3 | .flex-wrap(wrap);
4 | max-width: 520px;
5 |
6 | .controls {
7 | .box-sizing();
8 | .flex(1 1 50%);
9 | max-width: 240px;
10 | padding: 0 20px 20px 0;
11 | }
12 | }
13 | .colorPicker {
14 | position: relative;
15 | clear: both;
16 | display: inline-block;
17 | max-width: 100%;
18 | width: 100%;
19 |
20 | .track {
21 | background: url("@{touchui-base-path}img/colorpicker.png") no-repeat center center;
22 | height: 150px;
23 | width: 150px;
24 | padding: 10px;
25 | position: absolute;
26 | cursor: crosshair;
27 | left: auto;
28 | right: -71px;
29 | top: -71px;
30 | display: none;
31 | border: 0 none;
32 | z-index: 12;
33 | -webkit-border-radius: 150px;
34 | -moz-border-radius: 150px;
35 | border-radius: 150px;
36 | }
37 | .color {
38 | width: 30px;
39 | height: 100%;
40 | padding: 1px;
41 | border: 1px solid #ccc;
42 | display: block;
43 | position: absolute;
44 | z-index: 11;
45 | right: 0;
46 | top: 0;
47 | background-color: #efefef;
48 | -webkit-border-radius: 0 5px 5px 0;
49 | -moz-border-radius: 0 5px 5px 0;
50 | border-radius: 0 5px 5px 0;
51 | -webkit-box-sizing: border-box;
52 | -moz-box-sizing: border-box;
53 | box-sizing: border-box;
54 | cursor: pointer;
55 | }
56 | .colorInner {
57 | display: block;
58 | width: 26px;
59 | height: 100%;
60 | -webkit-border-radius: 0 5px 5px 0;
61 | -moz-border-radius: 0 5px 5px 0;
62 | border-radius: 0 5px 5px 0;
63 | }
64 | .dropdown {
65 | list-style: none;
66 | display: none;
67 | width: 27px;
68 | position: absolute;
69 | top: 28px;
70 | border: 1px solid #ccc;
71 | left: 0;
72 | z-index: 1000;
73 | li {
74 | height: 25px;
75 | cursor: pointer;
76 | }
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/source/less/fonts/sourcecodepro.less:
--------------------------------------------------------------------------------
1 | @font-face {
2 | font-family: 'Source Code Pro';
3 | font-style: normal;
4 | font-weight: 300;
5 | src: local('Source Code Pro Light'), local('SourceCodePro-Light'), url('@{sourcecodepro-path}SourceCodePro-Light.woff') format('woff');
6 | }
7 | @font-face {
8 | font-family: 'Source Code Pro';
9 | font-style: normal;
10 | font-weight: 400;
11 | src: local('Source Code Pro'), local('SourceCodePro-Regular'), url('@{sourcecodepro-path}SourceCodePro-Regular.woff') format('woff');
12 | }
13 | @font-face {
14 | font-family: 'Source Code Pro';
15 | font-style: normal;
16 | font-weight: 600;
17 | src: local('Source Code Pro Semibold'), local('SourceCodePro-Semibold'), url('@{sourcecodepro-path}SourceCodePro-Semibold.woff') format('woff');
18 | }
19 | @font-face {
20 | font-family: 'Source Code Pro';
21 | font-style: normal;
22 | font-weight: 700;
23 | src: local('Source Code Pro Bold'), local('SourceCodePro-Bold'), url('@{sourcecodepro-path}SourceCodePro-Bold.woff') format('woff');
24 | }
25 |
--------------------------------------------------------------------------------
/source/less/fonts/touchui.less:
--------------------------------------------------------------------------------
1 | @font-face {
2 | font-family: 'touchui';
3 | src:url('@{touchui-path}touchui.eot?-gkxfeg');
4 | src:url('@{touchui-path}touchui.eot?-gkxfeg#iefix') format('embedded-opentype'),
5 | url('@{touchui-path}touchui.ttf?-gkxfeg') format('truetype'),
6 | url('@{touchui-path}touchui.woff?-gkxfeg') format('woff'),
7 | url('@{touchui-path}touchui.svg?-gkxfeg#icomoon') format('svg');
8 | font-weight: normal;
9 | font-style: normal;
10 | }
11 |
--------------------------------------------------------------------------------
/source/less/layout/buttons.less:
--------------------------------------------------------------------------------
1 | button,
2 | .btn {
3 | border-color: darken(@btn-background, 10%);
4 | background: @btn-background;
5 | color: @btn-text;
6 | padding: 6px 15px;
7 |
8 | &:disabled {
9 | opacity: .4;
10 | }
11 | &:active {
12 | outline: 0 none;
13 | background: darken(@btn-background, 10%);
14 | color: @btn-text;
15 | }
16 |
17 | &-primary {
18 | border-color: darken(@btn-main-background, 10%);
19 | background: @btn-main-background;
20 | color: @btn-main-text;
21 |
22 | &:active {
23 | background: darken(@btn-main-background, 10%);
24 | color: @btn-main-text;
25 | }
26 | }
27 |
28 | &-danger {
29 | border-color: darken(@btn-danger-background, 10%);
30 | background: @btn-danger-background;
31 | color: @btn-danger-text;
32 |
33 | &:active {
34 | background: darken(@btn-danger-background, 10%);
35 | color: @btn-danger-text;
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/source/less/layout/footer.less:
--------------------------------------------------------------------------------
1 | .footer {
2 | .rem(font-size, 14);
3 | position: absolute;
4 | left: 0;
5 | bottom: 0;
6 | width: 100%;
7 | padding: 0 @main-gutter 0;
8 | .box-sizing(border-box);
9 |
10 | .flex-display(flex);
11 | .flex-direction(row);
12 | .flex-wrap(nowrap);
13 | .justify-content(space-between);
14 |
15 | ul {
16 | color: mix(@main-background, contrast(@main-background), 50%);
17 |
18 | .flex-order(0);
19 | .flex(0 0 auto);
20 | .align-self(auto);
21 |
22 | &.pull-left {
23 | .flex-order(0);
24 | .flex(1 1 auto);
25 | .align-self(auto);
26 | overflow: hidden;
27 | max-width: 100%;
28 |
29 | li {
30 | display: inline-block;
31 |
32 | small {
33 | .box-sizing(border-box);
34 | white-space: nowrap;
35 | text-overflow: ellipsis;
36 | overflow: hidden;
37 | padding-right: 10px;
38 | display: block;
39 | }
40 | }
41 | }
42 |
43 | li {
44 | margin-left: 0;
45 | .rem(line-height, 20);
46 |
47 | a {
48 | display: inline-block;
49 | .rem(width, 20);
50 | .rem(height, 20);
51 | color: mix(@main-background, contrast(@main-background), 50%);
52 | overflow: hidden;
53 | margin: 0;
54 | .rem(font-size, 18);
55 | padding: 0;
56 |
57 | &:hover {
58 | color: mix(@main-background, contrast(@main-background), 20%);
59 | text-decoration: none;
60 | }
61 | }
62 | }
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/source/less/layout/form.less:
--------------------------------------------------------------------------------
1 | input, select, textarea {
2 | background: @input-background;
3 | color: @input-text-color;
4 | text-align: left;
5 | opacity: .8;
6 | .rem(font-size, 14);
7 | .rem(line-height, 20);
8 |
9 | &:active, &:focus, &.ui-keyboard-input-current {
10 | opacity: 1;
11 | border-color: fade(@main-color, 90%);
12 | outline: 0;
13 | -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px fade(@main-color, 60%) !important;
14 | -moz-box-shadow: inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px fade(@main-color, 60%) !important;
15 | box-shadow: inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px fade(@main-color, 60%) !important;
16 | }
17 |
18 | &.disabled, &[disabled] {
19 | opacity: .4;
20 | }
21 |
22 | &[type="file"] {
23 | color: transparent !important;
24 | opacity: 0 !important;
25 | }
26 |
27 | }
28 |
29 | .uneditable-input {
30 | opacity: .4;
31 | }
32 |
33 | .input-append .add-on {
34 | .rem(font-size, 14);
35 | .rem(height, 20);
36 | .rem(line-height, 20);
37 | }
38 |
39 | legend {
40 | margin-bottom: 0;
41 | }
42 |
43 | .control-group {
44 | margin-top: 0;
45 | margin-bottom: 20px;
46 | }
--------------------------------------------------------------------------------
/source/less/layout/general.less:
--------------------------------------------------------------------------------
1 | .visible_touch {
2 | display: block;
3 |
4 | login_dropdown_loggedin {
5 | display: block !important;
6 | position: static;
7 | width: 100%;
8 | border: 0 none;
9 | margin: 0 !important;
10 | right: auto !important;
11 | padding: 0 !important;
12 | float: none;
13 | }
14 | }
15 | #navbar_login .hidden_touch {
16 | display: none !important;
17 | }
18 | .hidden_touch {
19 | display: none;
20 | }
21 |
22 | .accordion,
23 | #navbar .brand {
24 | position: absolute;
25 | top: -5000px;
26 | left: -5000px;
27 | }
28 |
29 | .page-container .octoprint-container .tab-content h1 {
30 | font-size: 1.3rem;
31 | line-height: 1.8rem;
32 | padding: 0 0 (@main-gutter / 2);
33 | }
34 |
35 | .touchui-accordion {
36 | .accordion-heading {
37 | a:extend(#touch body .page-container .octoprint-container .tab-content h1) {
38 | pointer-events: none;
39 |
40 | .fa {
41 | display: none;
42 | }
43 | }
44 | }
45 |
46 | .accordion-inner {
47 | padding: 0;
48 | }
49 | }
50 |
51 | .page-container {
52 | width: 100%;
53 |
54 | // &.no-pointer {
55 | // :active {
56 | // color: inherit !important;
57 | // background: inherit !important;
58 | // }
59 | // }
60 |
61 | .octoprint-container {
62 | margin: 0;
63 |
64 | * {
65 | border: 0 none;
66 | .border-radius(0) !important;
67 | }
68 |
69 | > .row {
70 | .rem(margin-bottom, 20);
71 | padding-bottom: 20px;
72 | }
73 |
74 | .tab-content {
75 | padding: @main-gutter @main-gutter 0;
76 | overflow: visible;
77 | .box-sizing(border-box);
78 |
79 | h1 {
80 | color: @text-color;
81 | margin: 0;
82 | }
83 |
84 | table {
85 | position: relative;
86 | margin-left: -@main-gutter;
87 | max-width: none;
88 | width: 100vw;
89 |
90 | tr {
91 | &:first-child {
92 | th {
93 | margin: 0;
94 | .rem(font-size, 18);
95 | border-bottom: 1px solid @main-background;
96 | background: @table-row-header-background;
97 | color: @table-row-header-text-color;
98 | padding: 13px 5px;
99 | }
100 | }
101 |
102 | td, th {
103 | &:first-child {
104 | padding-left: @main-gutter;
105 | }
106 | &:last-child {
107 | padding-right: @main-gutter;
108 | }
109 | }
110 |
111 | &:hover {
112 | background-color: @table-row-background;
113 | color: contrast(@table-row-background);
114 | }
115 |
116 | th, td {
117 | border-bottom: 1px solid @main-background;
118 | background-color: @table-row-background;
119 | color: contrast(@table-row-background);
120 | }
121 | }
122 | }
123 |
124 | }
125 |
126 | button, .btn {
127 | transition: background-color 0.2s ease, color 0.2s ease;
128 | background: @btn-main-background;
129 | color: @btn-main-text;
130 |
131 | &:active {
132 | background: darken(@btn-main-background, 10%);
133 | color: @btn-main-text;
134 | }
135 |
136 | &:focus {
137 | outline: 0 none;
138 | }
139 |
140 | &.disabled, &[disabled] {
141 | pointer-events: none;
142 | }
143 | }
144 |
145 | .add-on {
146 | background: @addon-background;
147 | color: @addon-text-color;
148 | border-left: 1px solid @main-background;
149 | }
150 |
151 | }
152 | }
153 |
154 | label, input, button, select, textarea, .input-mini {
155 | height: auto;
156 | .rem(font-size, 14);
157 | .rem(line-height, 20);
158 | }
159 |
160 | .control-group {
161 | .control-label,
162 | .controls {
163 | display: block;
164 | float: none;
165 | width: 100%;
166 | margin: 0;
167 | text-align: left;
168 | }
169 |
170 | }
171 |
172 | .tabbable,
173 | .container {
174 | width: 100%;
175 | }
176 |
177 | .container > .row,
178 | .container > .row > .tabbable {
179 | margin: 0;
180 | }
181 |
--------------------------------------------------------------------------------
/source/less/layout/header.less:
--------------------------------------------------------------------------------
1 | #navbar {
2 | background-color: @main-color;
3 | border: 0 none !important;
4 |
5 | .rem(height, 70);
6 | width: 100%;
7 |
8 | position: absolute;
9 | top: 0;
10 | left: 0;
11 |
12 | .navbar-inner {
13 | background: transparent !important;
14 | border: 0 none !important;
15 | padding: 0;
16 | margin: 0;
17 | }
18 | }
19 |
20 | .tabbable > .nav-tabs li,
21 | #navbar .nav > li {
22 | overflow: hidden;
23 | position: relative;
24 |
25 | > a {
26 | background: transparent;
27 | color: @main-color;
28 |
29 | .rem(height, 70);
30 | padding: 0;
31 | margin: 0;
32 |
33 | position: relative;
34 | z-index: 1000;
35 | white-space: nowrap;
36 | text-align: right;
37 |
38 | &:active {
39 | background: darken(@main-color, 15%);
40 | }
41 |
42 | &:before {
43 | .font-icon-reset();
44 | content: @fa-legacy-var-question;
45 | color: @main-color-text;
46 |
47 | text-align: center;
48 | .rem(font-size, 32);
49 | .rem(line-height, 70);
50 |
51 | display: block;
52 | height: 100%;
53 | width: 100%;
54 | position: absolute;
55 | left: 0;
56 | top: 0;
57 |
58 | @media(max-width: 320px) {
59 | font-size: 10vw !important;
60 | }
61 | }
62 | }
63 | touchui_dropdown_link a:before {
64 | content: none;
65 | }
66 | }
67 |
68 | #tabs {
69 | .flex-display();
70 | }
71 |
72 | #navbar .navbar-inner .container .nav li#all_touchui_settings {
73 | width: 200px;
74 | }
75 |
76 | #navbar,
77 | #tabs {
78 | .nav > li,
79 | &.nav > li {
80 | .flex(0 0 100%);
81 | max-width: 7rem;
82 |
83 | @media(max-width: 565px) {
84 | max-width: 5.5rem;
85 | }
86 |
87 | @media(max-width: 439px) {
88 | max-width: 5rem;
89 | }
90 |
91 | @media(max-width: 400px) {
92 | max-width: 4.5rem;
93 | }
94 |
95 | touchui_dropdown_link {
96 | pointer-events: none;
97 | }
98 | }
99 | }
100 |
--------------------------------------------------------------------------------
/source/less/layout/links.less:
--------------------------------------------------------------------------------
1 | a {
2 | color: @main-color;
3 |
4 | &:active {
5 | color: @main-color-darker;
6 | }
7 | &.active {
8 | background: @main-color;
9 | color: @main-color-text;
10 | }
11 | }
12 | li.active {
13 | a {
14 | background: @main-color;
15 | color: @main-color-text;
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/source/less/layout/navbar.less:
--------------------------------------------------------------------------------
1 | #navbar {
2 | z-index: 999;
3 |
4 | .navbar-inner {
5 |
6 | .container {
7 | .rem(height, 66);
8 | border:0 none;
9 |
10 | .nav {
11 | position: relative;
12 | z-index: 23;
13 |
14 | li {
15 | width: 100%;
16 |
17 | &.dropdown {
18 | margin: 0;
19 | z-index: 2;
20 |
21 | all_touchui_settings {
22 |
23 | > .dropdown-menu {
24 | min-width: 230px;
25 | }
26 |
27 | #touchui_text_nonlink_container {
28 | margin: 0;
29 | list-style: none;
30 | overflow: hidden;
31 | padding: 5px 0;
32 | }
33 |
34 | b.caret {
35 | display: none;
36 | }
37 |
38 | &.item_active {
39 | background: @main-background;
40 |
41 | > a:before {
42 | color: contrast(@main-background);
43 | }
44 | }
45 |
46 | &.offline > a:before {
47 | animation-name: blink_offline;
48 | }
49 |
50 | &.online > a:before {
51 | animation-name: blink_online;
52 | }
53 |
54 | > a:before {
55 | animation-duration: 0.6s;
56 | animation-iteration-count: 5;
57 | .font-icon-reset();
58 | content: @fa-legacy-var-navicon;
59 | }
60 |
61 | &.open {
62 | .dropdown-menu {
63 | .open {
64 | a.dropdown-toggle {
65 | background: transparent;
66 |
67 | &:after {
68 | .font-icon-reset();
69 | line-height: 1;
70 | content: @fa-legacy-var-caret-up !important;
71 | }
72 | }
73 | }
74 | }
75 | }
76 |
77 | li {
78 |
79 | &.offline {
80 | a:before {
81 | color: @offline-color;
82 | }
83 | }
84 |
85 | &.online {
86 | a:before {
87 | color: @online-color;
88 | }
89 | }
90 |
91 | conn_link2 {
92 | a:before {
93 | content: @fa-legacy-var-wifi;
94 | }
95 | }
96 |
97 | timelapse_link2 {
98 | a:before {
99 | .rem(font-size, 18);
100 | }
101 | }
102 |
103 | gcode_link2 {
104 | a:before {
105 | .rem(font-size, 17);
106 | }
107 | }
108 |
109 | navbar_settings {
110 | a:before {
111 | content: @fa-legacy-var-gear;
112 | }
113 | }
114 |
115 | navbar_plugin_announcements {
116 | display: none;
117 | }
118 |
119 | #usersettings_button {
120 | &:before {
121 | content: @fa-legacy-var-user;
122 | }
123 | }
124 |
125 | #logout_button {
126 | &:before {
127 | content: @fa-legacy-var-sign-out;
128 | }
129 | }
130 |
131 | #navbar_login {
132 | a.dropdown-toggle {
133 | &:after {
134 | .font-icon-reset();
135 | content: @fa-legacy-var-caret-down;
136 | position: absolute;
137 | right: 20px;
138 | margin: 0;
139 | .rem(line-height, 40);
140 | }
141 |
142 | &:before {
143 | content: @fa-legacy-var-lock;
144 | }
145 | }
146 | }
147 |
148 | navbar_plugin_touchui {
149 | a:before {
150 | content: @fa-legacy-var-mobile-phone;
151 | .rem(font-size, 28);
152 | .rem(line-height, 28);
153 | position: relative;
154 | top: 5px;
155 | margin-top: -9px;
156 | }
157 | }
158 |
159 | navbar_systemmenu {
160 | > a {
161 | position: relative;
162 | display: block;
163 | float: none;
164 |
165 | &.active {
166 | &:after {
167 | .font-icon-reset();
168 | content: @fa-legacy-var-caret-up !important;
169 | }
170 | }
171 |
172 | &:before {
173 | .font-icon-reset();
174 | content: @fa-legacy-var-gears;
175 | .rem(font-size, 19);
176 | }
177 | i {
178 | display: none;
179 | }
180 | &:after {
181 | .font-icon-reset();
182 | content: @fa-legacy-var-caret-down;
183 | right: 18px;
184 | position: absolute;
185 | top: 12px;
186 | line-height: 1;
187 | }
188 | }
189 | .dropdown-menu {
190 | a {
191 | &:before {
192 | .font-icon-reset();
193 | content: @fa-legacy-var-angle-double-right;
194 | }
195 | }
196 | }
197 | }
198 |
199 | a {
200 | .box-sizing(border-box);
201 |
202 | color: @main-color-text;
203 | display: block;
204 | text-align: left;
205 | width: 100% !important;
206 | max-width: 100% !important;
207 | height: auto !important;
208 | float: none;
209 |
210 | text-overflow: ellipsis;
211 | overflow: hidden;
212 |
213 | .icon-wrench{
214 | display: none;
215 | }
216 |
217 | &:active {
218 | background: darken(@main-color, 15%);
219 | }
220 |
221 | &:hover {
222 | background: transparent;
223 | }
224 |
225 | &:before {
226 | .font-icon-reset();
227 | content: @fa-legacy-var-question;
228 | display: inline-block;
229 | margin: 0 15px 0 0;
230 | .rem(font-size, 20);
231 | .rem(width, 20);
232 | position: relative;
233 | top: 2px;
234 | text-align: center;
235 | }
236 |
237 | }
238 |
239 | &:active > a {
240 | background: darken(@main-color, 15%);
241 | }
242 | }
243 | }
244 | }
245 |
246 | #youcanhazlogin {
247 | margin: 0;
248 | width: 100%;
249 | overflow: hidden;
250 |
251 | #navbar_login {
252 | &.open {
253 | #login_dropdown_loggedout {
254 | padding: 5px @dropdown-gutter @dropdown-gutter !important;
255 | display: block;
256 | }
257 | }
258 | }
259 |
260 | .pull-right small a {
261 | display: none;
262 | }
263 |
264 | #login_dropdown_loggedout {
265 | display: none;
266 | border-top: 1px solid @dropdown-divider-color;
267 | margin-top: 0;
268 | padding: 0;
269 | backface-visibility: hidden;
270 |
271 | * {
272 | .box-sizing(border-box);
273 | }
274 |
275 | input[type="text"],
276 | input[type="password"] {
277 | width: 100%;
278 | display: inline-block;
279 | height: 30px;
280 | padding-left: 0;
281 | padding-right: 0;
282 | text-indent: 12px;
283 | }
284 |
285 | &.hide + #login_dropdown_loggedin {
286 | display: block;
287 | }
288 | }
289 | }
290 |
291 | }
292 | }
293 | }
294 | }
295 |
296 | #navbar_plugin_touchui {
297 | float: none;
298 | }
299 |
300 | @keyframes blink_offline {
301 | 0% {
302 | color: @main-color-text;
303 | }
304 | 50% {
305 | color: @offline-color;
306 | }
307 | 100% {
308 | color: @main-color-text;
309 | }
310 | }
311 |
312 | @keyframes blink_online {
313 | 0% {
314 | color: @main-color-text;
315 | }
316 | 50% {
317 | color: @online-color;
318 | }
319 | 100% {
320 | color: @main-color-text;
321 | }
322 | }
323 |
324 | }
325 |
--------------------------------------------------------------------------------
/source/less/layout/tabbar.less:
--------------------------------------------------------------------------------
1 | #tabs,
2 | #navbar #all_touchui_settings {
3 | a:before {
4 | .font-icon-reset();
5 | }
6 |
7 | #navbar_plugin_pi_support {
8 | a:before {
9 | content: none;
10 | }
11 | span {
12 | margin-left: 5px;
13 | }
14 | }
15 |
16 | #temp_link,
17 | #temp_link2 {
18 | a:before {
19 | content: @fa-legacy-var-thermometer-2;
20 | }
21 | }
22 | #control_link,
23 | #control_link2 {
24 | a:before {
25 | content: @fa-legacy-var-gamepad;
26 | }
27 | }
28 | #gcode_link,
29 | #gcode_link2 {
30 | a:before {
31 | content: @fa-legacy-var-object-ungroup;
32 | }
33 | }
34 | #term_link,
35 | #term_link2 {
36 | a:before {
37 | content: @fa-legacy-var-terminal;
38 | }
39 | }
40 | #timelapse_link,
41 | #timelapse_link2 {
42 | a:before {
43 | content: @fa-legacy-var-camera;
44 | }
45 | }
46 | #webcam_link,
47 | #webcam_link2 {
48 | a:before {
49 | content: @fa-legacy-var-image;
50 | }
51 | }
52 | #print_link,
53 | #print_link2 {
54 | a:before {
55 | content: @fa-legacy-var-server;
56 | }
57 | }
58 |
59 | // Plugins
60 |
61 | #sidebar_plugin_filamentmanager_wrapper_link,
62 | #sidebar_plugin_filamentmanager_wrapper_link2 {
63 | a:before {
64 | content: @fa-legacy-var-tasks;
65 | }
66 | }
67 |
68 | #sidebar_plugin_estop_wrapper_link,
69 | #sidebar_plugin_estop_wrapper_link2 {
70 | a:before {
71 | content: @fa-legacy-var-exclamation-circle;
72 | }
73 | }
74 |
75 | #sidebar_plugin_bedlevelingwizard_wrapper_link,
76 | #sidebar_plugin_bedlevelingwizard_wrapper_link2 {
77 | a:before {
78 | content: @fa-legacy-var-arrows-alt;
79 | }
80 | }
81 |
82 | #sidebar_plugin_touchtest_wrapper_link,
83 | #sidebar_plugin_touchtest_wrapper_link2 {
84 | a:before {
85 | content: @fa-legacy-var-edit;
86 | }
87 | }
88 |
89 | #tab_plugin_enclosure_link,
90 | #tab_plugin_enclosure_link2 {
91 | a:before {
92 | content: @fa-legacy-var-cube;
93 | }
94 | }
95 |
96 | #tab_plugin_astroprint_link,
97 | #tab_plugin_astroprint_link2 {
98 | a:before {
99 | content: @fa-legacy-var-cloud;
100 | }
101 | }
102 |
103 | #navbar_plugin_discordremote,
104 | #navbar_plugin_discordremote2 {
105 | a:before {
106 | content: @fa-legacy-var-gamepad;
107 | }
108 | }
109 |
110 | #tab_plugin_dashboard_link,
111 | #tab_plugin_dashboard_link2 {
112 | a:before {
113 | content: @fa-legacy-var-tachometer;
114 | }
115 | }
116 |
117 | #tab_plugin_bedlevelvisualizer_link,
118 | #tab_plugin_bedlevelvisualizer_link2 {
119 | a:before {
120 | content: @fa-legacy-var-street-view;
121 | }
122 | }
123 |
124 | #tab_plugin_stlviewer_link,
125 | #tab_plugin_stlviewer_link2 {
126 | a:before {
127 | content: @fa-legacy-var-street-view;
128 | }
129 | }
130 |
131 | #sidebar_plugin_automaticshutdown_wrapper_link,
132 | #sidebar_plugin_automaticshutdown_wrapper_link2 {
133 | a:before {
134 | content: @fa-legacy-var-power-off;
135 | }
136 | }
137 | }
138 |
139 | #tabs {
140 | #webcam_link {
141 | .rem(font-size, 29);
142 | .rem(line-height, 73);
143 | }
144 |
145 | #print_link {
146 | .rem(line-height, 74);
147 | }
148 |
149 | // Pevent FOUT! (not dutch error!)
150 | &:first-child {
151 | &:before {
152 | font-family: "touchui";
153 | content: "";
154 | position: absolute;
155 | top: -5000px;
156 | }
157 | }
158 |
159 | &.nav-tabs {
160 | .itemActive {
161 | background: @main-background;
162 |
163 | a {
164 | .border-radius(0);
165 | color: @main-background;
166 |
167 | &:after,
168 | &:before {
169 | color: contrast(@main-background);
170 | }
171 | }
172 | }
173 | }
174 | }
175 |
--------------------------------------------------------------------------------
/source/less/layout/touchscreen.less:
--------------------------------------------------------------------------------
1 | &.isTouchscreenUI {
2 |
3 | .modal-backdrop {
4 | background: transparent !important;
5 | }
6 |
7 | .modal.fade {
8 | opacity: 1 !important;
9 | }
10 |
11 | #reloadui_overlay_background,
12 | #offline_overlay_background {
13 | background: transparent !important;
14 | }
15 |
16 | }
17 |
--------------------------------------------------------------------------------
/source/less/plugins/eeprommarlin.less:
--------------------------------------------------------------------------------
1 | #settings_plugin_eeprom_marlin ul.nav.nav-pills{
2 | display: block;
3 | }
--------------------------------------------------------------------------------
/source/less/plugins/history.less:
--------------------------------------------------------------------------------
1 | #tab_plugin_printhistory_link2 {
2 | a:before {
3 | .font-icon-reset();
4 | content: @fa-legacy-var-history !important;
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/source/less/plugins/m33fio.less:
--------------------------------------------------------------------------------
1 | #control {
2 | #control-jog-xy {
3 | > div {
4 | height: auto;
5 | left: auto;
6 | }
7 | }
8 |
9 | }
--------------------------------------------------------------------------------
/source/less/plugins/multicam.less:
--------------------------------------------------------------------------------
1 | #camControl {
2 | .flex-display();
3 | .flex-flow(column);
4 | padding: 20px 30px 0;
5 |
6 | > div {
7 | .flex-display();
8 | .flex-flow(row);
9 | width: 100%;
10 |
11 | button {
12 | width: auto;
13 | margin: 0 10px 0 0;
14 | }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/source/less/plugins/navbartemp.less:
--------------------------------------------------------------------------------
1 | #navbar_plugin_navbartemp {
2 | overflow: hidden;
3 |
4 | > div {
5 | margin: 0;
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/source/less/plugins/octolapse.less:
--------------------------------------------------------------------------------
1 | #navbar_plugin_octolapse {
2 | display: none;
3 | }
--------------------------------------------------------------------------------
/source/less/plugins/printerstatistics.less:
--------------------------------------------------------------------------------
1 | #tab_plugin_stats_link2 {
2 | a:before {
3 | .font-icon-reset();
4 | content: @fa-legacy-var-area-chart !important;
5 | }
6 | }
7 |
8 | #tab_plugin_stats {
9 | table {
10 | tr {
11 | td, &:hover {
12 | background: transparent;
13 | }
14 | }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/source/less/plugins/psucontrol.less:
--------------------------------------------------------------------------------
1 | @psucontrol-on-color: @online-color;
2 | @psucontrol-off-color: #808080;
3 |
4 | #navbar .navbar-inner .container .nav li.dropdown#all_touchui_settings li #psucontrol_indicator {
5 | &:before {
6 | .font-icon-reset();
7 | content: @fa-legacy-var-bolt;
8 | }
9 |
10 | &.on:before {
11 | color: @psucontrol-on-color;
12 | }
13 |
14 | &.off:before {
15 | color: @psucontrol-off-color;
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/source/less/settings/touchui.less:
--------------------------------------------------------------------------------
1 | /* Modal TouchUI (settings modal) */
2 | #settings_plugin_touchui {
3 |
4 | div.disabled {
5 | opacity: 0.4;
6 | pointer-events: none;
7 | }
8 |
9 | legend {
10 | label {
11 | margin-top: 12px;
12 |
13 | @media (max-width: 400px) {
14 | float: none;
15 | display: block;
16 | margin: 0;
17 | }
18 | }
19 | }
20 |
21 | .track {
22 | .close {
23 | width: 25px;
24 | height: 25px;
25 | background: @main-color;
26 | border: 1px solid @main-color-darker;
27 | position: absolute;
28 | top: 15px;
29 | right: 15px;
30 | border-radius: 50%;
31 | opacity: 1;
32 |
33 | &:after {
34 | .font-icon-reset();
35 | content: @fa-legacy-var-close;
36 | color: @main-color-text;
37 | text-align: center;
38 | display: block;
39 | .rem(line-height, 25);
40 | }
41 | }
42 | }
43 |
44 | }
45 |
46 | /* Modal TouchUI (not settings) */
47 | #touchui_settings_dialog {
48 |
49 | .btn-box {
50 | .flex-display();
51 | .flex-wrap(wrap);
52 | margin: 10px 0;
53 |
54 | &:first-child {
55 | margin-top: 0;
56 | }
57 | }
58 |
59 | button {
60 | border: 0 none;
61 | .border-radius(0);
62 | }
63 |
64 | .label {
65 | .flex-display();
66 | .align-items(center);
67 | .rem(font-size, 14);
68 | .rem(line-height, 20);
69 | .border-radius(0);
70 | padding: 6px 9px;
71 | }
72 |
73 | }
74 |
75 | /* Nabar */
76 | #navbar_plugin_touchui {
77 | float: right;
78 |
79 | #navbar_touchui_settings {
80 | i:before {
81 | .font-icon-reset();
82 | content: @fa-legacy-var-mobile;
83 | .rem(font-size, 28);
84 | position: relative;
85 | top: 5px;
86 | margin-top: -9px;
87 | }
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/source/less/tabs/connection.less:
--------------------------------------------------------------------------------
1 | #connection {
2 | height: auto !important;
3 | }
4 | #connection_wrapper {
5 |
6 | button {
7 | margin-top: 20px;
8 | background-color: @main-color;
9 | color: @main-color-text;
10 |
11 | &:active {
12 | background-color: @main-color-dark;
13 | }
14 | }
15 |
16 | }
17 |
--------------------------------------------------------------------------------
/source/less/tabs/gcode.less:
--------------------------------------------------------------------------------
1 | #gcode {
2 | #gcode_canvas {
3 | width: 100% !important;
4 | height: 100% !important;
5 |
6 | // Disable hard all events
7 | pointer-events: none !important;
8 | }
9 |
10 | .slider {
11 | display: none;
12 | }
13 |
14 | .progress {
15 | margin: 30px 0 20px;
16 | width: 100%;
17 | }
18 |
19 | @media (max-width: 500px) {
20 | .span7, .span5 {
21 | float: none;
22 | width: 100%;
23 | display: block;
24 | margin: 0;
25 | padding: 0;
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/source/less/tabs/printer.less:
--------------------------------------------------------------------------------
1 | #printer {
2 |
3 | .accordion {
4 | &-heading {
5 | display: none;
6 | }
7 | &-inner {
8 | padding: 0;
9 | background: transparent !important;
10 |
11 | hr {
12 | display: none;
13 | }
14 | }
15 | }
16 |
17 | .print-control {
18 | .btn {
19 | .flex-display();
20 | .align-items(center);
21 | .rem(height, 50);
22 | padding: 0;
23 | overflow: hidden;
24 | }
25 | i {
26 | .rem(font-size, 28);
27 | .flex(1 1 100%);
28 | min-width: 100%;
29 | }
30 | .icon-print:before {
31 | .font-icon-reset();
32 | content: @fa-legacy-var-step-forward;
33 | }
34 | #job_cancel,
35 | #job_pause {
36 | background: @text-color;
37 | color: contrast(@text-color);
38 |
39 | &:active {
40 | background: fade(@text-color, 70%);
41 | color: contrast(@text-color);
42 | }
43 | }
44 | #job_pause {
45 | top: 40px;
46 | }
47 | #job_cancel {
48 | top: 80px;
49 | }
50 | }
51 |
52 | #state {
53 | padding-top: ~"calc(3.125rem + 60px) !important";
54 |
55 | .progress,
56 | .print-control {
57 | position: absolute !important;
58 | top: ~"calc(3.125rem + 15px)";
59 | left: 0;
60 | height: 20px;
61 | width: 100%;
62 | margin: 0;
63 | }
64 | .print-control {
65 | top: 0;
66 | }
67 | .progress {
68 | top: ~"calc(3.125rem + 15px) !important";
69 | }
70 | }
71 |
72 | .slimScrollBar, .slimScrollRail {
73 | display: none !important;
74 | }
75 | .dropdown-menu {
76 | left: auto !important;
77 | right: -10px !important;
78 | }
79 |
80 | }
81 |
82 | .progress-text-centered {
83 | top: auto !important;
84 | }
85 |
--------------------------------------------------------------------------------
/source/less/tabs/temperature.less:
--------------------------------------------------------------------------------
1 | #temp {
2 | margin-left: -@main-gutter;
3 | margin-right: -@main-gutter;
4 | overflow: hidden;
5 |
6 | position: absolute;
7 | left: -99999px;
8 | top: -99999px;
9 | display: block;
10 | max-width: 100vw;
11 |
12 | &.active {
13 | position: relative;
14 | left: auto;
15 | top: auto;
16 | }
17 |
18 | > div:first-child {
19 | .box-sizing(border-box);
20 | overflow: hidden;
21 |
22 | width: 100%;
23 | height: 60vh;
24 | max-height: none;
25 | min-height: 200px;
26 |
27 | padding: 0 @main-gutter;
28 | margin: 0 0 @main-gutter;
29 |
30 | #temperature-graph {
31 | .box-sizing(border-box);
32 | position: relative;
33 |
34 | background-position: center 37%;
35 | background-repeat: no-repeat;
36 | background-size: auto 80%;
37 |
38 | width: 100%;
39 | height: 100%;
40 | margin: 0;
41 |
42 | .tickLabel, .legendLabel {
43 | color: contrast(@main-background);
44 | }
45 |
46 | table {
47 | width: auto;
48 |
49 | tr {
50 | background: transparent !important;
51 |
52 | &:hover {
53 | background: transparent !important;
54 |
55 | td {
56 | background: transparent !important;
57 | }
58 | }
59 |
60 | td {
61 | background: transparent !important;
62 | color: contrast(@main-color);
63 | border: 0 none;
64 |
65 | &.legendLabel {
66 | padding: 0 15px 0 5px;
67 | }
68 | }
69 | }
70 | }
71 | }
72 | }
73 |
74 |
75 | > div:last-child {
76 | .box-sizing(border-box);
77 |
78 | table {
79 | margin: 0 !important;
80 | width: 100%;
81 |
82 | tr {
83 | &:first-child {
84 | th {
85 | .rem(font-size, 18);
86 |
87 | &:first-child {
88 | width: 10% !important;
89 | }
90 | &:nth-child(3) {
91 | width: auto !important;
92 | }
93 | @media (max-width: 730px) {
94 | &:last-child {
95 | width: 38vw !important;
96 | .box-sizing();
97 | }
98 | }
99 | }
100 | }
101 |
102 | th, td {
103 | .rem(font-size, 16);
104 | padding: 12px;
105 |
106 | &:nth-child(1) {
107 | text-align: right;
108 | padding-left: 0;
109 | }
110 |
111 | &:nth-child(2) {
112 | text-align: center !important;
113 | }
114 |
115 | /*&:nth-child(3) {
116 | padding-right: 30px;
117 | }*/
118 |
119 | .dropdown-menu {
120 | margin: 2px 0 9px;
121 | bottom: 100%;
122 | top: auto;
123 | left: auto;
124 | right: 0;
125 |
126 | &:before {
127 | border: 10px solid transparent;
128 | border-top-color: @main-color;
129 | border-bottom-color: transparent !important;
130 | position: absolute;
131 | top: auto;
132 | bottom: -18px;
133 | right: 9px;
134 | }
135 | }
136 |
137 | &.temperature_offset {
138 | .form-inline {
139 | .input-append {
140 | .flex-wrap(wrap);
141 | max-width: 100%;
142 | }
143 | }
144 | }
145 |
146 | .form-inline,
147 | .input-append {
148 | .box-sizing(border-box);
149 |
150 | .flex-display(flex);
151 | .flex-direction(row);
152 | .flex-wrap(wrap);
153 | .justify-content(flex-start);
154 | .align-content(stretch);
155 | .align-items(flex-start);
156 |
157 | > * {
158 | .flex-order(0);
159 | .flex(0 1 auto);
160 | .align-self(auto);
161 | margin-top: 2px;
162 | margin-bottom: 2px;
163 | }
164 |
165 | > .input-append {
166 | .flex(1 1 0%);
167 | .flex-wrap();
168 | padding-right: 10px;
169 | margin-bottom: 0;
170 | margin-top: 0;
171 | }
172 |
173 | input, .input-mini {
174 | .flex-order(0);
175 | .flex(1 1 14px);
176 | .align-self(auto);
177 |
178 | margin-right: 0px;
179 | padding: 7px 0 7px 15px;
180 | .border-radius(0);
181 | text-align: left;
182 |
183 | width: 0% !important;
184 | max-width: none !important;
185 | min-width: 0px;
186 | }
187 | .btn-group,
188 | > button {
189 | .border-radius(0);
190 |
191 | .fa, .icon {
192 | pointer-events: none;
193 | }
194 | }
195 | .add-on {
196 | padding: 7px;
197 | margin-right: 1px;
198 | .rem(font-size, 14);
199 | .rem(line-height, 20);
200 | height: auto;
201 |
202 | @media (max-width: 474px) {
203 | display: none;
204 | }
205 | }
206 | button {
207 | padding: 7px 15px;
208 | .border-radius(0);
209 |
210 | .caret {
211 | width: 11px;
212 |
213 | &:after {
214 | content: @fa-legacy-var-caret-down;
215 | .font-icon-reset();
216 | .rem(font-size, 18);
217 | top: -7px;
218 | position: relative;
219 | }
220 | }
221 | }
222 | }
223 | }
224 | }
225 | }
226 |
227 | @media (max-width: 730px) {
228 | width: 138vw !important;
229 | margin: 0 0 @main-gutter 0 !important;
230 | transition: left 0.5s ease;
231 | cursor: w-resize;
232 | position: relative;
233 | left: 0;
234 |
235 | &.open {
236 | left: -38vw !important;
237 | }
238 | }
239 | }
240 |
241 | #temperature-table {
242 | @media (max-width: 490px) {
243 | td,
244 | th {
245 | .rem(font-size, 14) !important;
246 | }
247 | }
248 |
249 | td {
250 | .btn {
251 | border-left: 1px solid @table-row-background;
252 | }
253 | }
254 |
255 | th.temperature_target {
256 | .btn-group {
257 | float: none !important;
258 | margin-top: -5px;
259 |
260 | .caret:after {
261 | content: @fa-legacy-var-caret-down;
262 | .font-icon-reset();
263 | font-size: 1.125rem;
264 | top: -7px;
265 | left: -6px;
266 | position: relative;
267 | }
268 | }
269 | }
270 |
271 | th.temperature_target,
272 | th.temperature_offset {
273 | text-align: left;
274 | }
275 | }
276 | }
277 |
--------------------------------------------------------------------------------
/source/less/tabs/terminal.less:
--------------------------------------------------------------------------------
1 | #term {
2 | position: relative;
3 | z-index: 1;
4 |
5 | .icon-caret-down:before,
6 | .icon-caret-right:before {
7 | .font-icon-reset();
8 | content: @fa-legacy-var-chevron-down;
9 | }
10 | .icon-caret-right:before {
11 | content: @fa-legacy-var-chevron-right;
12 | }
13 |
14 | span.muted {
15 | color: fade(@terminal-color, 50%);
16 | }
17 |
18 | .row-fluid {
19 | clear: both;
20 | padding-top: 5px;
21 | }
22 |
23 | ::-webkit-scrollbar-thumb {
24 | background: @terminal-color;
25 | }
26 |
27 | a {
28 | color: @terminal-color;
29 | }
30 |
31 | button,
32 | .btn {
33 | background: @terminal-color;
34 | color: contrast(@terminal-color, @dark-color, @light-color, 51%);
35 |
36 | &:active {
37 | background: transparent;
38 | color: @terminal-color;
39 | }
40 | }
41 |
42 | .terminal {
43 |
44 | small {
45 | &.pull-left {
46 | margin-right: 30px;
47 |
48 | span {
49 | display: block !important;
50 | padding: 10px 0 0 0 !important;
51 |
52 | + span {
53 | display: none !important;;
54 | }
55 | }
56 | }
57 | &.pull-right {
58 | position: absolute;
59 | right: 0;
60 | }
61 |
62 | button,
63 | .btn {
64 | padding: 3px 10px;
65 | background: transparent;
66 | color: @terminal-color;
67 |
68 | + span {
69 | padding-left: 7px;
70 | }
71 | &.active {
72 | background: @terminal-color;
73 | color: contrast(@terminal-color, @dark-color, @light-color, 51%);
74 | }
75 | }
76 | }
77 | a:not(.btn) {
78 | .rem(font-size, 35);
79 |
80 | i:before {
81 | .font-icon-reset();
82 | }
83 | }
84 |
85 | button,
86 | .btn {
87 | padding: 4px 12px;
88 | background: @main-background;
89 | color: contrast(@main-background);
90 |
91 | &.active {
92 | background: @terminal-color;
93 | color: contrast(@terminal-color, @dark-color, @light-color, 51%);
94 | }
95 | }
96 | }
97 |
98 | #terminal-filterpanel,
99 | #termin-filterpanel {//TODO remove #termin in future; backwards compatibility with <1.3.0
100 | width: 100%;
101 | }
102 |
103 | #terminal-scroll {
104 | border: 1px solid @terminal-color;
105 | overflow: hidden;
106 | .rem(margin-bottom, 28);
107 | .border-radius(5px, 5px, 0, 0);
108 | height: 69vh;
109 | min-height: 150px;
110 | background: @terminal-background;
111 | z-index: 2;
112 | }
113 |
114 | #terminal-output,
115 | #terminal-output-lowfi {
116 | height: auto;
117 | min-height: 0;
118 | max-height: none;
119 | .box-sizing(border-box);
120 | margin-bottom: 26px;
121 | border: 0 none;
122 | margin: 0;
123 | padding: 10px @terminal-gutter @terminal-gutter;
124 | overflow: hidden;
125 | color: @terminal-text-color;
126 | background-color: transparent;
127 | font-size: 0.8rem;
128 | line-height: 1.4rem;
129 |
130 | &:before {
131 | font-family: "touchui";
132 | content: "\e601\e605\e604\e603\e602\e600";
133 | display: block;
134 | width: 100%;
135 | margin: 5px auto 0;
136 | position: relative;
137 | .rem(font-size, 34);
138 | .rem(line-height, 62);
139 | letter-spacing: 3px;
140 | text-align: center;
141 |
142 | @media (max-width: 350px) {
143 | font-size: 10vw;
144 | }
145 |
146 | @media (max-width: 280px) {
147 | font-size: 8vw;
148 | }
149 |
150 | }
151 |
152 | span:first-child {
153 | &:before {
154 | content: "v?.?.?";
155 | display: block;
156 | position: relative;
157 | left: 0;
158 | margin: -5px 0 20px;
159 | .rem(line-height, 17);
160 | .rem(font-size, 14);
161 | width: 100%;
162 | text-align: center;
163 | }
164 | }
165 | }
166 | #terminal-sendpanel {
167 | position: absolute;
168 | left: 0;
169 | width: 100%;
170 | margin: 0;
171 | top: 69vh;
172 | .rem(font-size, 14);
173 |
174 | .muted {
175 | display: none;
176 | }
177 |
178 | input, button, .btn {
179 | &:disabled, .disabled {
180 | opacity: 1;
181 | background: mix(@terminal-color, @main-background, 30%);
182 | color: @terminal-color;
183 | }
184 | }
185 | .input-append {
186 | position: relative;
187 | }
188 | input {
189 | width: 100%;
190 | background-color: @main-background;
191 | color: @terminal-color;
192 | border: 1px solid @terminal-color;
193 | padding: 0;
194 | display: block;
195 | z-index: 2;
196 |
197 | .rem(font-size, 14);
198 | .rem(height, 30);
199 | text-indent: 10px;
200 | padding-right: 90px;
201 |
202 | .box-sizing(border-box);
203 | .border-radius(0, 0, 5px, 5px);
204 | }
205 | button, .btn {
206 | position: absolute;
207 | top: 0;
208 | right: 1px;
209 | background-color: @terminal-color;
210 | color: @main-background;
211 | border: 0 none;
212 | border-top: 1px solid @terminal-color;
213 | border-left: 1px solid @terminal-color;
214 | .text-shadow(0 0 0 transparent);
215 | .border-radius(0, 0, 5px, 0);
216 | .box-sizing();
217 | .rem(height, 30);
218 | .rem(line-height, 20);
219 | .rem(font-size, 18);
220 | z-index: 21;
221 | padding: 4px 12px;
222 | }
223 |
224 | .btn {
225 | width: auto;
226 | }
227 | }
228 | }
229 |
230 | #scroll {
231 | #term {
232 | .iScrollIndicator {
233 | background: @terminal-iScroll-color !important;
234 | border: 0 none !important;
235 | }
236 | }
237 | }
238 |
--------------------------------------------------------------------------------
/source/less/tabs/timelapse.less:
--------------------------------------------------------------------------------
1 | #timelapse {
2 |
3 | > h1 {
4 | margin: 30px 0 0;
5 | }
6 |
7 | .icon-unchecked:before,
8 | .icon-check-empty:before {
9 | .font-icon-reset();
10 | content: @fa-legacy-var-copy;
11 | }
12 |
13 | .caret {
14 | display: none;
15 | }
16 |
17 | .pull-left, .pull-right {
18 | button {
19 | font-size: 1.1rem;
20 | padding: 10px 15px;
21 | }
22 | }
23 |
24 | input[type="text"],
25 | input[type="number"],
26 | input[type="password"],
27 | select {
28 | width: 100%;
29 | margin-bottom: 20px;
30 | text-align: left;
31 | }
32 |
33 | .help-block {
34 | color: fade(contrast(@main-background), 80%);
35 | padding: 0;
36 | font-size: 0.9em;
37 | line-height: 1rem;
38 | }
39 |
40 | .input-append {
41 | position: relative;
42 | margin-bottom: 20px;
43 |
44 | input[type="text"],
45 | input[type="number"],
46 | input[type="password"] {
47 | width: 100%;
48 | padding: 10px 50px 10px 10px;
49 | margin: 0 !important;
50 | height: 32px;
51 | .box-sizing(border-box);
52 | .border-radius(0);
53 | }
54 |
55 | .add-on {
56 | border-left: 1px solid @main-background;
57 | position: absolute;
58 | right: 0;
59 | padding: 6px;
60 | }
61 | }
62 |
63 | .checkbox {
64 | margin-bottom: 20px;
65 | }
66 |
67 | > table {
68 | tr {
69 | .timelapse_files_action {
70 | width: 75px;
71 | text-align: right;
72 | }
73 | .timelapse_files_size {
74 | text-align: center;
75 | }
76 |
77 | td {
78 | .rem(line-height, 40);
79 |
80 | a {
81 | padding: 6px;
82 | margin: -6px;
83 | display: inline-block;
84 | color: @link-color;
85 |
86 | &:before {
87 | .rem(font-size, 21);
88 | .rem(line-height, 36);
89 | }
90 | &:active {
91 | color: @link-color;
92 | }
93 | }
94 | }
95 |
96 | }
97 |
98 | }
99 |
100 | }
101 |
--------------------------------------------------------------------------------
/source/less/tabs/webcam.less:
--------------------------------------------------------------------------------
1 | #webcam {
2 | margin: (-@main-gutter + 1px) -@main-gutter 0;
3 |
4 | #webcam_rotator,
5 | #webcam_rotator .webcam_fixed_ratio .webcam_fixed_ratio_inner {
6 | display: flex;
7 | align-content: center;
8 | align-items: center;
9 | justify-content: center;
10 | }
11 |
12 | #webcam_rotator .webcam_fixed_ratio {
13 | padding: 0;
14 | height: 100%;
15 | width: 100%;
16 | }
17 |
18 | #webcam_container {
19 | background: @main-background;
20 |
21 | .text {
22 | color: @text-color;
23 | }
24 | }
25 |
26 | #webcam_image {
27 | display: block;
28 | width: 100%;
29 | height: auto;
30 | }
31 |
32 | .muted {
33 | padding: 10px 35px;
34 | display: block;
35 | }
36 |
37 | .rotate90,
38 | .webcam_rotated .webcam_fixed_ratio {
39 | transform: rotate(0deg) !important;
40 | height: auto;
41 |
42 | #webcam_image {
43 | transform: rotate(-90deg) !important;
44 | max-width: ~"calc(100vh - 69px)";
45 |
46 | &.flipV {
47 | transform: rotate(-90deg) scaleX(-1) !important;
48 | }
49 | &.flipH {
50 | transform: rotate(-90deg) scaleY(-1) !important;
51 | }
52 | &.flipV.flipH {
53 | transform: rotate(-90deg) scaleY(-1) scaleX(-1) !important;
54 | }
55 | }
56 | }
57 | .webcam_rotated .webcam_fixed_ratio {
58 | padding: 0;
59 | }
60 |
61 | @supports (object-fit: contain) {
62 | #webcam_rotator,
63 | #webcam_rotator .webcam_fixed_ratio .webcam_fixed_ratio_inner {
64 | height: ~"calc(100vh - 69px)";
65 | }
66 |
67 | #webcam_image {
68 | object-fit: contain;
69 | display: block;
70 | width: 100%;
71 | height: auto;
72 | max-height: 100%;
73 | max-width: 100%;
74 | }
75 | }
76 |
77 | }
78 |
--------------------------------------------------------------------------------
/source/less/touchui.less:
--------------------------------------------------------------------------------
1 | @import "_variables.less";
2 | @import "_mixins.less";
3 |
4 | @import "fonts/sourcecodepro.less";
5 | @import "fonts/touchui.less";
6 | @import "fonts/fontawesome.less";
7 |
8 | @import "settings/touchui.less";
9 | @import "components/tinycolorpicker.less";
10 |
11 | #touch {
12 | width: 100%;
13 | height: 100%;
14 | background: @main-background;
15 | font-size: @main-font-size + 0px;
16 |
17 | @import "components/emulate.touch.less";
18 | @import "components/scrollbars.less";
19 | @import "layout/touchscreen.less";
20 |
21 | body {
22 | width: 100%;
23 | height: auto;
24 | min-height: 100%;
25 | background: @main-background;
26 | color: @text-color;
27 | position: relative;
28 | .rem(font-size, 14);
29 | .rem(line-height, 20);
30 |
31 | * {
32 | .text-shadow(none) !important;
33 | .box-shadow(none) !important;
34 |
35 | &:not([class^="icon-"]):not([class*=" icon-"]):not(.fa):not(.far):not(.fas):not(.fab) {
36 | font-family: 'Source Code Pro', monospace;
37 | }
38 | }
39 |
40 | .no-pointer * {
41 | pointer-events: none !important;
42 | }
43 |
44 | #touchui_auto_load_resolution,
45 | #touchui_auto_load_touch {
46 | display: none;
47 | }
48 |
49 | div.error {
50 | background: transparent !important;
51 | color: inherit !important;
52 | }
53 |
54 | .touchui_disabled {
55 | opacity: 0.7;
56 | pointer-events: none;
57 | }
58 |
59 | #settings-appearanceCloseModalsWithClick span {
60 | font-size: 11px;
61 | }
62 |
63 | @import "layout/header.less";
64 | @import "layout/general.less";
65 | @import "layout/links.less";
66 | @import "layout/buttons.less";
67 | @import "layout/navbar.less";
68 | @import "layout/tabbar.less";
69 | @import "layout/form.less";
70 |
71 | @import "tabs/webcam.less";
72 | @import "tabs/printer.less";
73 | @import "tabs/terminal.less";
74 | @import "tabs/temperature.less";
75 | @import "tabs/controls.less";
76 | @import "tabs/gcode.less";
77 | @import "tabs/connection.less";
78 | @import "tabs/timelapse.less";
79 |
80 | @import "components/progress.less";
81 | @import "components/overlays.less";
82 | @import "components/files.less";
83 | @import "components/modal.less";
84 | @import "components/scroll.less";
85 | @import "components/keyboard.less";
86 | @import "components/dropdown.less";
87 | @import "components/notifications.less";
88 | @import "components/pagination.less";
89 | @import "components/popover.less";
90 |
91 | @import "plugins/navbartemp.less";
92 | @import "plugins/history.less";
93 | @import "plugins/printerstatistics.less";
94 | @import "plugins/psucontrol.less";
95 | @import "plugins/m33fio.less";
96 | @import "plugins/eeprommarlin.less";
97 | @import "plugins/octolapse.less";
98 | @import "plugins/multicam.less";
99 |
100 | @import "layout/footer.less";
101 |
102 | // For phone users
103 | #files_wrapper .entry,
104 | #temp .row-fluid table {
105 | .transform(translateZ(0));
106 | .user-select(none);
107 | }
108 |
109 | }
110 | }
111 |
--------------------------------------------------------------------------------
/source/svg/C.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/source/svg/H.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/source/svg/O.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/source/svg/T.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
6 |
7 |
9 |
10 |
11 |
13 |
14 |
--------------------------------------------------------------------------------
/source/svg/U.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/source/svg/UI.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
6 |
7 |
9 |
11 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/source/svg/fan.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/source/vendors/tinycolorpicker/lib/jquery.tinycolorpicker.min.js:
--------------------------------------------------------------------------------
1 | /*! tinycolorpicker - v0.9.4 - 2015-11-20
2 | * http://www.baijs.com/tinycolorpicker
3 | *
4 | * Copyright (c) 2015 Maarten Baijs ;
5 | * Licensed under the MIT license */
6 |
7 | !function(a){"function"==typeof define&&define.amd?define(["jquery"],a):"object"==typeof exports?module.exports=a(require("jquery")):a(jQuery)}(function(a){function b(b,e){function f(){return s?(m=a(" "),k.append(m),q=m[0].getContext("2d"),g()):a.each(j.options.colors,function(a,b){var c=p.clone();c.css("backgroundColor",b),c.attr("data-color",b),o.append(c)}),h(),j}function g(){var b=new Image,c=k.css("background-image").replace(/"/g,"").replace(/url\(|\)$/gi,"");b.crossOrigin="Anonymous",k.css("background-image","none"),a(b).on("load",function(){m.attr("width",this.width),m.attr("height",this.height),q.drawImage(b,0,0,this.width,this.height)}),b.src=j.options.backgroundUrl||c}function h(){var b=t?"touchstart":"mousedown";s?(l.bind(b,function(b){b.preventDefault(),b.stopPropagation(),k.toggle(),a(document).bind("mousedown.colorpicker",function(b){a(document).unbind(".colorpicker"),j.close()})}),t?(m.bind("touchstart",function(a){return r=!0,i(a.originalEvent.touches[0]),!1}),m.bind("touchmove",function(a){return i(a.originalEvent.touches[0]),!1}),m.bind("touchend",function(a){return j.close(),!1})):(m.mousedown(function(b){return r=!0,i(b),a(document).bind("mouseup.colorpicker",function(b){return a(document).unbind(".colorpicker"),j.close(),!1}),!1}),m.mousemove(i))):(l.bind("mousedown",function(a){a.preventDefault(),a.stopPropagation(),o.toggle()}),o.delegate("li","mousedown",function(b){b.preventDefault(),b.stopImmediatePropagation();var c=a(this).attr("data-color");j.setColor(c),o.hide()}))}function i(c){if(r){var d=a(c.target),e=d.offset(),f=q.getImageData(c.pageX-e.left,c.pageY-e.top,1,1).data;j.setColor("rgb("+f[0]+","+f[1]+","+f[2]+")"),b.trigger("change",[j.colorHex,j.colorRGB])}}this.options=a.extend({},d,e),this._defaults=d,this._name=c;var j=this,k=b.find(".track"),l=b.find(".color"),m=null,n=b.find(".colorInput"),o=b.find(".dropdown"),p=o.find("li").remove(),q=null,r=!1,s=!!document.createElement("canvas").getContext,t="ontouchstart"in document.documentElement;return this.colorHex="",this.colorRGB="",this.setColor=function(a){a.indexOf("#")>=0?(j.colorHex=a,j.colorRGB=j.hexToRgb(j.colorHex)):(j.colorRGB=a,j.colorHex=j.rgbToHex(j.colorRGB)),l.find(".colorInner").css("backgroundColor",j.colorHex),n.val(j.colorHex)},this.close=function(){r=!1,k.hide()},this.hexToRgb=function(a){var b=/^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(a);return"rgb("+parseInt(b[1],16)+","+parseInt(b[2],16)+","+parseInt(b[3],16)+")"},this.rgbToHex=function(a){function b(a){var b=new Array("0","1","2","3","4","5","6","7","8","9","A","B","C","D","E","F");return isNaN(a)?"00":b[(a-a%16)/16]+b[a%16]}var c=a.match(/\d+/g);return"#"+b(c[0])+b(c[1])+b(c[2])},f()}var c="tinycolorpicker",d={colors:["#ffffff","#A7194B","#FE2712","#FB9902","#FABC02","#FEFE33","#D0EA2B","#66B032","#0391CE","#0247FE","#3D01A5","#8601AF"],backgroundUrl:null};a.fn[c]=function(d){return this.each(function(){a.data(this,"plugin_"+c)||a.data(this,"plugin_"+c,new b(a(this),d))})}});
--------------------------------------------------------------------------------
/source/vendors/tinycolorpicker/lib/tinycolorpicker.min.js:
--------------------------------------------------------------------------------
1 | /*! tinycolorpicker - v0.9.4 - 2015-11-20
2 | * http://www.baijs.com/tinycolorpicker
3 | *
4 | * Copyright (c) 2015 Maarten Baijs ;
5 | * Licensed under the MIT license */
6 |
7 | !function(a){"use strict";function b(){for(var a=1;a=0?(k.colorHex=a,k.colorRGB=k.hexToRgb(k.colorHex)):(k.colorRGB=a,k.colorHex=k.rgbToHex(k.colorRGB)),n.style.backgroundColor=k.colorHex,p.value=k.colorHex},this.close=function(){r=!1,l.style.display="none"},this.hexToRgb=function(a){var b=/^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(a);return"rgb("+parseInt(b[1],16)+","+parseInt(b[2],16)+","+parseInt(b[3],16)+")"},this.rgbToHex=function(a){function b(a){var b=new Array("0","1","2","3","4","5","6","7","8","9","A","B","C","D","E","F");return isNaN(a)?"00":b[(a-a%16)/16]+b[a%16]}var c=a.match(/\d+/g);return"#"+b(c[0])+b(c[1])+b(c[2])},g()}var d="tinycolorpicker",e={backgroundUrl:null},f=function(a,b){return new c(a,b)};"function"==typeof define&&define.amd?define(function(){return f}):"object"==typeof module&&module.exports?module.exports=f:a.tinycolorpicker=f}(window);
--------------------------------------------------------------------------------