├── .editorconfig
├── .github
└── workflows
│ └── main.yml
├── .gitignore
├── CODE_OF_CONDUCT.md
├── LICENSE
├── README.md
├── app.qml
├── build
├── compiler
├── __init__.py
├── doc
│ ├── __init__.py
│ └── json.py
├── gcc
│ ├── COPYING
│ ├── README.md
│ └── compiler.jar
├── grammar.py
├── js
│ ├── __init__.py
│ ├── code.py
│ ├── component.py
│ └── generator.py
├── lang.py
├── manifest.py
└── ts.py
├── core
├── .manifest
├── Anchors.qml
├── Animation.qml
├── BaseLayout.qml
├── BaseLayoutContentPadding.qml
├── BaseMixin.qml
├── BaseMouseMixin.qml
├── BaseView.qml
├── BaseViewContent.qml
├── Binding.qml
├── Border.qml
├── BorderSide.qml
├── ClickMixin.qml
├── ColorAnimation.qml
├── Column.qml
├── Component.qml
├── Connection.qml
├── ContentMargin.qml
├── Context.qml
├── ContextMenuMixin.qml
├── Device.qml
├── Effects.qml
├── EventEmitter.qml
├── Font.qml
├── Gamepad.qml
├── GamepadManager.qml
├── Gradient.qml
├── GradientStop.qml
├── Grid.qml
├── GridView.qml
├── HoverMixin.qml
├── Image.qml
├── Item.qml
├── Layout.qml
├── ListModel.qml
├── ListView.qml
├── Loader.qml
├── LocalStorage.qml
├── Location.qml
├── Model.qml
├── MouseArea.qml
├── MouseMoveMixin.qml
├── MousePressMixin.qml
├── Object.qml
├── PageStack.qml
├── PropertyStorage.qml
├── ProxyModel.qml
├── RAIIEventEmitter.qml
├── Radius.qml
├── Rectangle.qml
├── Repeater.qml
├── Request.qml
├── Row.qml
├── ScrollView.qml
├── SequentialAnimation.qml
├── Shadow.qml
├── System.qml
├── TableView.qml
├── Text.qml
├── Timer.qml
├── Transform.qml
├── VideoPlayer.qml
├── WheelMixin.qml
├── core.js
├── gradient.js
├── model.js
└── transform.js
├── generate-gamepad-mappings
├── package-lock.json
├── package.json
├── partners.json
├── platform
├── android
│ ├── .core.js
│ ├── .manifest
│ ├── build.py
│ ├── device.js
│ └── dist
│ │ ├── androidIcon.png
│ │ ├── config.xml
│ │ └── index.html
├── commercial
│ ├── .core.js
│ ├── .manifest
│ ├── PureQmlSplash.qml
│ └── dist
│ │ └── res
│ │ ├── pureqml-splash-logo.png
│ │ └── pureqml-splash-shadow.png
├── core
│ └── .manifest
├── debug.console.re
│ ├── .core.js
│ ├── .manifest
│ └── dist
│ │ └── index.html
├── electronjs
│ ├── .core.js
│ ├── .manifest
│ ├── device.js
│ └── dist
│ │ ├── main.js
│ │ ├── package.json
│ │ ├── preload.js
│ │ └── renderer.js
├── html5.canvas
│ ├── .core.js
│ ├── .manifest
│ └── backend.js
├── html5.fingerprint
│ ├── .manifest
│ ├── fingerprint.js
│ └── sha1.js
├── html5
│ ├── .core.js
│ ├── .manifest
│ ├── Stylesheet.qml
│ ├── cache.js
│ ├── dist
│ │ ├── index.html
│ │ └── modernizr-custom.js
│ ├── html.js
│ ├── localstorage.js
│ └── location.js
├── ios
│ ├── .core.js
│ ├── .manifest
│ ├── build.py
│ ├── device.js
│ ├── dist
│ │ ├── config.xml
│ │ ├── icons
│ │ │ ├── icon-1024.png
│ │ │ ├── icon-20.png
│ │ │ ├── icon-20@2x.png
│ │ │ ├── icon-20@3x.png
│ │ │ ├── icon-24@2x.png
│ │ │ ├── icon-27.5@2x.png
│ │ │ ├── icon-29.png
│ │ │ ├── icon-29@2x.png
│ │ │ ├── icon-29@3x.png
│ │ │ ├── icon-40.png
│ │ │ ├── icon-40@2x.png
│ │ │ ├── icon-44@2x.png
│ │ │ ├── icon-50.png
│ │ │ ├── icon-50@2x.png
│ │ │ ├── icon-60@2x.png
│ │ │ ├── icon-60@3x.png
│ │ │ ├── icon-72.png
│ │ │ ├── icon-72@2x.png
│ │ │ ├── icon-76.png
│ │ │ ├── icon-76@2x.png
│ │ │ ├── icon-83.5@2x.png
│ │ │ ├── icon-86@2x.png
│ │ │ ├── icon-98@2x.png
│ │ │ ├── icon.png
│ │ │ ├── icon@2x.png
│ │ │ └── splash
│ │ │ │ ├── Default@2x~ipad~anyany.png
│ │ │ │ ├── Default@2x~ipad~comany.png
│ │ │ │ ├── Default@2x~iphone~anyany.png
│ │ │ │ ├── Default@2x~iphone~comany.png
│ │ │ │ ├── Default@2x~iphone~comcom.png
│ │ │ │ ├── Default@3x~iphone~anyany.png
│ │ │ │ ├── Default@3x~iphone~anycom.png
│ │ │ │ └── Default@3x~iphone~comany.png
│ │ └── index.html
│ └── video.js
├── loggerpanel
│ ├── .core.js
│ └── .manifest
├── pure.blessed
│ ├── .core.js
│ ├── .manifest
│ └── backend.js
├── pure.femto
│ ├── .core.js
│ ├── .manifest
│ ├── backend.js
│ ├── build-android-native.sh
│ ├── device.js
│ ├── location.js
│ ├── storage.js
│ └── video.js
├── pure.void
│ ├── .core.js
│ ├── .manifest
│ └── backend.js
├── pure
│ ├── .core.js
│ ├── .manifest
│ ├── Stylesheet.qml
│ └── runtime.js
├── video.dashjs
│ ├── .core.js
│ ├── .manifest
│ ├── backend.js
│ └── dist
│ │ ├── dash.all.min.js
│ │ └── index.html
├── video.html5
│ ├── .core.js
│ ├── .manifest
│ └── backend.js
├── video.jsmpeg
│ ├── .core.js
│ ├── .manifest
│ ├── backend.js
│ └── dist
│ │ ├── index.html
│ │ ├── jsmpeg.min.js
│ │ └── m3u8-parser.min.js
├── video.shaka
│ ├── .core.js
│ ├── .manifest
│ ├── backend.js
│ └── dist
│ │ ├── index.html
│ │ └── shaka-player.compiled.min.js
├── video.videojs
│ ├── .core.js
│ ├── .manifest
│ ├── backend.js
│ └── dist
│ │ ├── index.html
│ │ └── video.min.js
├── web.pwa
│ ├── .core.js
│ ├── .manifest
│ └── dist
│ │ └── sw.js
├── web
│ ├── .core.js
│ ├── .manifest
│ └── device.js
└── webextension
│ ├── .manifest
│ └── dist
│ ├── icon128.png
│ ├── icon16.png
│ ├── icon32.png
│ ├── icon48.png
│ ├── index.html
│ └── manifest.json
├── requirements.txt
├── test
├── Makefile
├── lit.cfg.py
├── lit.py
├── lit
│ ├── BooleanExpression.py
│ ├── LitConfig.py
│ ├── LitTestCase.py
│ ├── ProgressBar.py
│ ├── ShCommands.py
│ ├── ShUtil.py
│ ├── Test.py
│ ├── TestRunner.py
│ ├── TestingConfig.py
│ ├── __init__.py
│ ├── builtin_commands
│ │ ├── __init__.py
│ │ ├── cat.py
│ │ └── diff.py
│ ├── cl_arguments.py
│ ├── discovery.py
│ ├── display.py
│ ├── formats
│ │ ├── __init__.py
│ │ ├── base.py
│ │ ├── googletest.py
│ │ └── shtest.py
│ ├── llvm
│ │ ├── __init__.py
│ │ ├── config.py
│ │ └── subst.py
│ ├── main.py
│ ├── reports.py
│ ├── run.py
│ ├── util.py
│ └── worker.py
├── model.js
├── qml
│ ├── enum_and_ids_in_string_context.qml
│ ├── expr.qml
│ ├── float_parse.qml
│ ├── gh172-a.qml
│ ├── gh172-b.qml
│ ├── gh205.qml
│ ├── model_row.qml
│ └── underscore_in_property_type.qml
├── update.js
└── view.js
└── update-ts
/.editorconfig:
--------------------------------------------------------------------------------
1 | # EditorConfig is awesome: http://EditorConfig.org
2 |
3 | # top-most EditorConfig file
4 | root = false
5 |
6 | [*]
7 | end_of_line = lf
8 | insert_final_newline = true
9 | charset = utf-8
10 | indent_style = tab
11 | indent_size = 4
12 | trim_trailing_whitespace = true
13 |
--------------------------------------------------------------------------------
/.github/workflows/main.yml:
--------------------------------------------------------------------------------
1 | # This is a basic workflow to help you get started with Actions
2 |
3 | name: CI
4 |
5 | # Controls when the workflow will run
6 | on:
7 | # Triggers the workflow on push or pull request events but only for the master branch
8 | push:
9 | branches: [ master ]
10 | pull_request:
11 | branches: [ master ]
12 |
13 | # Allows you to run this workflow manually from the Actions tab
14 | workflow_dispatch:
15 |
16 | # A workflow run is made up of one or more jobs that can run sequentially or in parallel
17 | jobs:
18 | # This workflow contains a single job called "build"
19 | build:
20 | # The type of runner that the job will run on
21 | runs-on: ubuntu-latest
22 |
23 | # Steps represent a sequence of tasks that will be executed as part of the job
24 | steps:
25 | # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
26 | - uses: actions/checkout@v4
27 |
28 | # Runs a single command using the runners shell
29 | - name: Run lit tests
30 | run: |
31 | pip install -r requirements.txt
32 | make -C test
33 |
34 | - name: Run mocha tests
35 | run: |
36 | npm install
37 | npm test
38 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.swp
2 | *.swo
3 | *.pyc
4 | *.pyo
5 | app/qml.js
6 | app/qml.min.js
7 | .cache/*
8 | node_modules
9 | .idea/
10 | test/qml/Output
11 | test/build.*
12 | test/.cache
13 |
14 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
6 |
7 | ## Our Standards
8 |
9 | Examples of behavior that contributes to creating a positive environment include:
10 |
11 | * Using welcoming and inclusive language
12 | * Being respectful of differing viewpoints and experiences
13 | * Gracefully accepting constructive criticism
14 | * Focusing on what is best for the community
15 | * Showing empathy towards other community members
16 |
17 | Examples of unacceptable behavior by participants include:
18 |
19 | * The use of sexualized language or imagery and unwelcome sexual attention or advances
20 | * Trolling, insulting/derogatory comments, and personal or political attacks
21 | * Public or private harassment
22 | * Publishing others' private information, such as a physical or electronic address, without explicit permission
23 | * Other conduct which could reasonably be considered inappropriate in a professional setting
24 |
25 | ## Our Responsibilities
26 |
27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
28 |
29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
30 |
31 | ## Scope
32 |
33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
34 |
35 | ## Enforcement
36 |
37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at team@pureqml.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
38 |
39 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
40 |
41 | ## Attribution
42 |
43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
44 |
45 | [homepage]: http://contributor-covenant.org
46 | [version]: http://contributor-covenant.org/version/1/4/
47 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2015-2020 PureQML Team
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/app.qml:
--------------------------------------------------------------------------------
1 | Text {
2 | anchors.fill: context;
3 |
4 | text: 'Hello, world!';
5 | font.pixelSize: 32;
6 | verticalAlignment: Text.AlignVCenter;
7 | horizontalAlignment: Text.AlignHCenter;
8 | }
9 |
--------------------------------------------------------------------------------
/compiler/doc/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pureqml/qmlcore/4b1eff3a3478ba8ddc51e363e99c6808212094db/compiler/doc/__init__.py
--------------------------------------------------------------------------------
/compiler/gcc/compiler.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pureqml/qmlcore/4b1eff3a3478ba8ddc51e363e99c6808212094db/compiler/gcc/compiler.jar
--------------------------------------------------------------------------------
/compiler/js/__init__.py:
--------------------------------------------------------------------------------
1 | from builtins import map
2 |
3 | import re
4 |
5 | def split_name(name):
6 | dot = name.rfind('.')
7 | if dot >= 0:
8 | return name[:dot], name[dot + 1:]
9 | else:
10 | return '', name
11 |
12 | def get_package(name):
13 | return split_name(name)[0]
14 |
15 | _escape_re = re.compile(r'\W')
16 | def escape(name):
17 | return _escape_re.sub('_', name)
18 |
19 | _id_re = re.compile(r'[^a-zA-Z0-9_]')
20 |
21 | def escape_id(name):
22 | return _id_re.sub('_', name)
23 |
24 | def escape_package(name):
25 | package = name.split('.')
26 | return ".".join(map(escape_id, package))
27 |
28 | def mangle_package(name):
29 | package = name.split('.')
30 | package = list(map(escape_id, package))
31 | if package[0] == '_globals':
32 | package.pop(0)
33 | package = [''] + package
34 | return "$".join(package)
35 |
36 | class Error(Exception):
37 | def __init__(self, message, loc):
38 | if loc:
39 | super(Error, self).__init__("{}: {}".format(loc, message))
40 | else:
41 | super(Error, self).__init__(message)
42 |
43 | from compiler.js.component import component_generator
44 | from compiler.js.generator import generator
45 |
--------------------------------------------------------------------------------
/compiler/manifest.py:
--------------------------------------------------------------------------------
1 | from builtins import object
2 |
3 | import json
4 |
5 | def _get_property(obj, name, default = None):
6 | if '.' in name:
7 | path = name.split('.')
8 | for k in path[:-1]:
9 | obj = obj.get(k, {})
10 | return obj.get(path[-1], default)
11 | else:
12 | return obj.get(name, default)
13 |
14 | def _set_property(obj, name, value):
15 | if '.' in name:
16 | path = name.split('.')
17 | current = obj
18 | for p in path[:-1]:
19 | current = current.setdefault(p, {})
20 | current[path[-1]] = value
21 | else:
22 | obj[name] = value
23 |
24 | def _pair_hook(pairs):
25 | obj = {}
26 | for k, v in pairs:
27 | _set_property(obj, k, v)
28 | return obj
29 |
30 | def merge_properties(dst, src):
31 | src = _pair_hook(list(src.items()))
32 | for key, value in src.items():
33 | if key.find('.') >= 0:
34 | raise Exception('dot found in key')
35 | if isinstance(value, dict):
36 | node = dst.setdefault(key, {})
37 | merge_properties(node, value)
38 | else:
39 | dst[key] = value
40 |
41 | return dst
42 |
43 | class Manifest(object):
44 | def __init__(self, data = None):
45 | if data is None:
46 | data = {}
47 | self.data = data
48 |
49 | @property
50 | def source_dir(self):
51 | return self.data.get('sources', 'src')
52 |
53 | @property
54 | def apps(self):
55 | return self.data.get('apps', [])
56 |
57 | @property
58 | def web_prefix(self):
59 | return self.data.get('web-prefix', '')
60 |
61 | @property
62 | def strict(self):
63 | return self.data.get('strict', True)
64 |
65 | @property
66 | def standalone(self):
67 | return self.data.get('standalone', True)
68 |
69 | @property
70 | def requires(self):
71 | return self.data.get('requires', [])
72 |
73 | @property
74 | def use_only_for(self):
75 | return self.data.get('use-only-for', [])
76 |
77 | def platform_requires(self, platform):
78 | return _get_property(self.data, 'platform.%s.requires' %platform, [])
79 |
80 | @property
81 | def minify(self):
82 | return self.data.get('minify', False)
83 |
84 | @property
85 | def templater(self):
86 | return self.data.get('templater', 'simple')
87 |
88 | @property
89 | def languages(self):
90 | return self.data.get('languages', [])
91 |
92 | @property
93 | def platforms(self):
94 | return self.data.get('platforms', [])
95 |
96 | @property
97 | def package(self):
98 | return self.data.get('package', '')
99 |
100 | @property
101 | def public(self):
102 | return self.data.get('public', False)
103 |
104 | @property
105 | def templates(self):
106 | return self.data.get('templates', ['*.html'])
107 |
108 | @property
109 | def properties(self):
110 | return self.data.setdefault('properties', {})
111 |
112 | def set_property(self, name, value):
113 | return _set_property(self.properties, name, value)
114 |
115 | @property
116 | def partner(self):
117 | return self.data.get('partner', 'free')
118 |
119 | @property
120 | def export_module(self):
121 | return self.data.get('export_module', False)
122 |
123 | def load(f):
124 | return Manifest(json.load(f, object_pairs_hook = _pair_hook))
125 |
126 | def loads(s):
127 | return Manifest(json.loads(s, object_pairs_hook = _pair_hook))
128 |
--------------------------------------------------------------------------------
/core/.manifest:
--------------------------------------------------------------------------------
1 | {
2 | "package": "core",
3 | "properties": {
4 | "resolutionWidth": 0,
5 | "resolutionHeight": 0,
6 | "useNativeFocusForInput": true,
7 | "emulateRemoteKeys": false
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/core/Animation.qml:
--------------------------------------------------------------------------------
1 | /**
2 | controls property animation behavior in declarative way
3 | Animation class works only for integral types, please use ColorAnimation for animating color properties
4 | */
5 | Object {
6 | property int delay: 0; ///< delay in ms
7 | property int duration: 200; ///< duration in ms
8 | property bool cssTransition: true; ///< use css transition if possible
9 | property bool running: false; ///< currently running
10 | property string easing: "ease"; ///< easing function
11 | property Object target; ///< target object
12 | property string property; ///< target object property
13 | property variant from; ///< used in declarative animation inside SequentialAnimation, starting value
14 | property variant to; ///< used in declarative animation inside SequentialAnimation, destination value
15 |
16 | constructor: { this._disabled = 0; this._native = false }
17 | /// disable animation.
18 | disable: { ++this._disabled; this._updateAnimation() }
19 | /// enable animation.
20 | enable: { --this._disabled; this._updateAnimation() }
21 | /// returns true if animation is enabled
22 | enabled: { return this._disabled === 0 }
23 |
24 | /// @private
25 | onDelayChanged,
26 | onDurationChanged,
27 | onCssTransitionChanged,
28 | onRunningChanged,
29 | onEasingChanged: { this._updateAnimation() }
30 |
31 | function active() {
32 | return this.enabled() && this.duration > 0
33 | }
34 |
35 | function _updateAnimation() {
36 | if (this.target)
37 | this.target.updateAnimation(this.property, this)
38 | }
39 |
40 | /// @private
41 | function interpolate(dst, src, t) {
42 | return t * (dst - src) + src;
43 | }
44 |
45 | /// @private
46 | function complete() { }
47 |
48 | }
49 |
--------------------------------------------------------------------------------
/core/BaseLayout.qml:
--------------------------------------------------------------------------------
1 | /// base class for BaseView and Layout
2 | Item {
3 | property int count; ///< number of children elements
4 | property bool trace; ///< output debug info in logs: layouts, item positioning
5 |
6 | property int spacing; ///< spacing between adjanced items, pixels
7 | property int currentIndex; ///< index of current focused item
8 | property int contentWidth; ///< content width
9 | property int contentHeight; ///< content height
10 | property bool keyNavigationWraps; ///< key navigation wraps from first to last and vise versa
11 | property bool handleNavigationKeys; ///< handle navigation keys, move focus
12 | property int layoutDelay: -1; ///< <0 - end of the tick (default), 0 - request animation frame, >0 - delay in ms
13 | property int prerenderDelay: -1; ///< <0 - end of the tick (default), 0 - request animation frame, >0 - delay in ms
14 | property bool offlineLayout; ///< layout delegates even if view's invisible
15 | property lazy padding: BaseLayoutContentPadding { } //< padding for content
16 |
17 | ///@private
18 | constructor: {
19 | this._skipPositioning = false
20 | this._padding = {}
21 | }
22 |
23 | /// @private
24 | function _attach() { }
25 |
26 | ///@private
27 | function _scheduleLayout(skipPositioning) {
28 | if (!this.recursiveVisible && !this.offlineLayout)
29 | return
30 |
31 | if (skipPositioning)
32 | this._skipPositioning = true
33 |
34 | if (this.prerenderDelay >= 0 && this.layoutDelay >= 0 && this.layoutDelay < this.prerenderDelay) {
35 | this._context.delayedAction('layout', this, this._doLayoutNP, this.layoutDelay)
36 | this._context.delayedAction('prerender', this, this._doLayout, this.prerenderDelay)
37 | } else
38 | this._context.delayedAction('layout', this, this._doLayout, this.layoutDelay)
39 | }
40 |
41 | ///@private
42 | function _doLayout() {
43 | this._attach()
44 | this._processUpdates()
45 | this._layout()
46 | this._skipPositioning = false
47 | }
48 |
49 | ///@private
50 | function _doLayoutNP() {
51 | this._attach()
52 | this._processUpdates()
53 | this._layout(true)
54 | this._skipPositioning = false
55 | }
56 |
57 | ///@private
58 | function _processUpdates() { }
59 |
60 | onSpacingChanged,
61 | onRecursiveVisibleChanged: {
62 | this._scheduleLayout()
63 | }
64 |
65 | ///@private
66 | onCompleted: { this._scheduleLayout() }
67 | }
68 |
--------------------------------------------------------------------------------
/core/BaseLayoutContentPadding.qml:
--------------------------------------------------------------------------------
1 | /// class controlling internal paddings of BaseLayout content
2 | Object {
3 | property int top: all; ///< top padding
4 | property int left: all; ///< left padding
5 | property int right: all; ///< right padding
6 | property int bottom: all; ///< bottom padding
7 | property int all; ///< a value for all sides
8 |
9 | prototypeConstructor: {
10 | BaseLayoutContentPaddingPrototype.defaultProperty = 'all'
11 | }
12 |
13 | constructor: {
14 | this.parent._padding = this
15 | }
16 |
17 | onTopChanged,
18 | onLeftChanged,
19 | onRightChanged,
20 | onBottomChanged: { this.parent._scheduleLayout(); }
21 | }
22 |
--------------------------------------------------------------------------------
/core/BaseMixin.qml:
--------------------------------------------------------------------------------
1 | Object {
2 | property bool enabled: true; ///< enable/disable mixin
3 |
4 | constructor: {
5 | this.element = this.parent.element
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/core/BaseMouseMixin.qml:
--------------------------------------------------------------------------------
1 | BaseMixin {
2 | property string cursor; ///< mouse cursor
3 |
4 | ///@private
5 | constructor: {
6 | this._touchEvent = false
7 | if (this.cursor)
8 | this.parent.style('cursor', this.cursor)
9 | }
10 |
11 | function _setTouchEvent() {
12 | this._touchEvent = true
13 | }
14 |
15 | function _resetTouchEvent() {
16 | this._touchEvent = false
17 | }
18 |
19 | /// @private pops touch event flag to skip mouse over later
20 | function _trueUnlessTouchEvent() {
21 | return !this._touchEvent
22 | }
23 |
24 | onCursorChanged: {
25 | this.parent.style('cursor', value)
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/core/BaseViewContent.qml:
--------------------------------------------------------------------------------
1 | /// @private content for base view scrolling area
2 | Item {
3 | onXChanged: { this.parent._scheduleLayout() } //fixme: if you need sync _layout here, please note that discarding delegate can result in recursive createDelegate() call from _layout, do not change it without fixing that first.
4 | onYChanged: { this.parent._scheduleLayout() }
5 |
6 | constructor: {
7 | this.style('will-change', 'scroll-position, transform, left, top')
8 | }
9 |
10 | ///@private silently updates scroll positions, because browser animates scroll
11 | function _updateScrollPositions(x, y, layout) {
12 | this._setProperty('x', -x)
13 | this._setProperty('y', -y)
14 | if (layout === undefined || layout) //default true
15 | this.parent._scheduleLayout(true) //schedule layout but skip positioning
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/core/Binding.qml:
--------------------------------------------------------------------------------
1 | Object {
2 | property bool delayed; ///< delays update to the next tick
3 | property string property; ///< target property
4 | property Object target; ///< target object
5 | property bool when: true; ///< assign value to target when this condition met
6 | property var value; ///< any value
7 |
8 | function _updateTarget() {
9 | if (this.delayed)
10 | this._context.delayedAction("binding:update", this, this._updateTargetImpl)
11 | else
12 | this._updateTargetImpl()
13 | }
14 |
15 | function _updateTargetImpl() {
16 | $core.assign(this.target, this.property, this.value)
17 | }
18 |
19 | onTargetChanged, onValueChanged, onWhenChanged: {
20 | if (this.target && this.when && this.property)
21 | this._updateTarget()
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/core/Border.qml:
--------------------------------------------------------------------------------
1 | /// class controlling border rendering
2 | Object {
3 | property int width; ///< width of the border
4 | property color color: "black"; ///< color of the border
5 | property enum style { None, Hidden, Dotted, Dashed, Solid, Double, Groove, Ridge, Inset, Outset }: Solid; ///< style of the border
6 | property enum type { Inner, Outer, Center }; ///< whether box is inside bounding rect or not
7 |
8 | property lazy left: BorderSide { name: "left"; } ///< left border side
9 | property lazy right: BorderSide { name: "right"; } ///< right border side
10 | property lazy top: BorderSide { name: "top"; } ///< top border side
11 | property lazy bottom: BorderSide { name: "bottom"; } ///< bottom border side
12 |
13 | function _update() {
14 | var parent = this.parent
15 | var value = this.width
16 | parent.style('border-width', value)
17 | switch(this.type) {
18 | case this.Inner:
19 | parent._borderXAdjust = 0
20 | parent._borderYAdjust = 0
21 | parent._borderInnerWidthAdjust = -2 * value
22 | parent._borderInnerHeightAdjust = -2 * value
23 | parent._setSizeAdjust()
24 | break
25 | case this.Outer:
26 | parent._borderXAdjust = -value
27 | parent._borderYAdjust = -value
28 | parent._borderWidthAdjust = 0
29 | parent._borderHeightAdjust = 0
30 | parent._setSizeAdjust()
31 | break
32 | case this.Center:
33 | parent._borderXAdjust = -value / 2
34 | parent._borderYAdjust = -value / 2
35 | parent._borderWidthAdjust = -value
36 | parent._borderHeightAdjust = -value
37 | parent._setSizeAdjust()
38 | break
39 | }
40 | }
41 |
42 | onWidthChanged: {
43 | this._update()
44 | }
45 |
46 | onTypeChanged: {
47 | var style
48 | switch(value) {
49 | case this.Inner:
50 | style = 'border-box'; break;
51 | case this.Outer:
52 | case this.Center:
53 | style = 'content-box'; break;
54 | }
55 | this.parent.style('box-sizing', style)
56 | this._update()
57 | }
58 |
59 | onColorChanged: {
60 | var newColor = $core.Color.normalize(this.color)
61 | this.parent.style('border-color', newColor)
62 | }
63 |
64 | onStyleChanged: {
65 | var styleName
66 | switch(value) {
67 | case this.None: styleName = 'none'; break
68 | case this.Hidden: styleName = 'hidden'; break
69 | case this.Dotted: styleName = 'dotted'; break
70 | case this.Dashed: styleName = 'dashed'; break
71 | case this.Solid: styleName = 'solid'; break
72 | case this.Double: styleName = 'double'; break
73 | case this.Groove: styleName = 'groove'; break
74 | case this.Ridge: styleName = 'ridge'; break
75 | case this.Inset: styleName = 'inset'; break
76 | case this.Outset: styleName = 'outset'; break
77 | }
78 |
79 | this.parent.style('border-style', styleName)
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/core/BorderSide.qml:
--------------------------------------------------------------------------------
1 | /**
2 | @private
3 | Component aimed to adjust individual preferences of each border side
4 | */
5 |
6 | Object {
7 | // property enum type { Inner, Outer, Center }; ///< whether box is inside bounding rect or not
8 | property string name;
9 | property int width: parent.width;
10 | property color color: parent.color;
11 | property int style: parent.style;
12 | property int type: parent.type;
13 |
14 | ///@private
15 | function _updateStyle() {
16 | if (!this.parent || !this.parent.parent || !this.name)
17 | return
18 |
19 | var Border = $core.Border
20 | var styleName
21 | switch(this.style) {
22 | case Border.None: styleName = 'none'; break
23 | case Border.Hidden: styleName = 'hidden'; break
24 | case Border.Dotted: styleName = 'dotted'; break
25 | case Border.Dashed: styleName = 'dashed'; break
26 | case Border.Solid: styleName = 'solid'; break
27 | case Border.Double: styleName = 'double'; break
28 | case Border.Groove: styleName = 'groove'; break
29 | case Border.Ridge: styleName = 'ridge'; break
30 | case Border.Inset: styleName = 'inset'; break
31 | case Border.Outset: styleName = 'outset'; break
32 | }
33 |
34 | var borderCss = this.width + "px " + styleName + " " + $core.Color.normalize(this.color)
35 | this.parent.parent.style('border-' + this.name, borderCss)
36 | }
37 |
38 | ///@private
39 | onWidthChanged,
40 | onColorChanged,
41 | onStyleChanged: { this._updateStyle() }
42 | }
43 |
--------------------------------------------------------------------------------
/core/ClickMixin.qml:
--------------------------------------------------------------------------------
1 | /// This mixin provides mouse click event detecting
2 | BaseMouseMixin {
3 | ///@private
4 | constructor: {
5 | this._bindClick(this.enabled)
6 | }
7 |
8 | ///@private
9 | function _bindClick(value) {
10 | if (value && !this._cmClickBinder) {
11 | this._cmClickBinder = new $core.EventBinder(this.element)
12 | this._cmClickBinder.on('click', $core.createSignalForwarder(this.parent, 'clicked').bind(this))
13 | }
14 | if (this._cmClickBinder)
15 | this._cmClickBinder.enable(value)
16 | }
17 |
18 | ///@private
19 | onEnabledChanged: { this._bindClick(value) }
20 | }
21 |
--------------------------------------------------------------------------------
/core/ColorAnimation.qml:
--------------------------------------------------------------------------------
1 | ///Animation for color-typed properties
2 | Animation {
3 | ///@private
4 | function interpolate(dst, src, t) {
5 | return $core.Color.interpolate(dst, src, t)
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/core/Column.qml:
--------------------------------------------------------------------------------
1 | /// Layout for vertical oriented content
2 | Layout {
3 | ///@private
4 | onKeyPressed: {
5 | if (!this.handleNavigationKeys)
6 | return false;
7 |
8 | switch (key) {
9 | case 'Up': return this.focusPrevChild()
10 | case 'Down': return this.focusNextChild()
11 | }
12 | }
13 |
14 | ///@private
15 | function _layout() {
16 | if (!this.recursiveVisible && !this.offlineLayout)
17 | return
18 |
19 | var children = this.children;
20 | var p = 0
21 | var w = 0
22 | this.count = children.length
23 |
24 | for(var i = 0; i < children.length; ++i) {
25 | var c = children[i]
26 | if (!('height' in c))
27 | continue
28 |
29 | var tm = c.anchors.topMargin || c.anchors.margins
30 | var bm = c.anchors.bottomMargin || c.anchors.margins
31 |
32 | var r = c.x + c.width
33 | if (r > w)
34 | w = r
35 | c.viewY = p + tm
36 | if (c.visible && c.height > 0)
37 | p += c.height + tm + bm + this.spacing
38 | }
39 | if (p > 0)
40 | p -= this.spacing
41 | this.contentWidth = w
42 | this.contentHeight = p
43 | }
44 |
45 | ///@private
46 | function addChild(child) {
47 | $core.Item.prototype.addChild.apply(this, arguments)
48 |
49 | if (!('height' in child))
50 | return
51 |
52 | var update = this._scheduleLayout.bind(this)
53 | child.onChanged('height', update)
54 | child.onChanged('recursiveVisible', update)
55 | child.on('anchorsMarginsUpdated', update)
56 | this._scheduleLayout()
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/core/Component.qml:
--------------------------------------------------------------------------------
1 | /// Semi-stub to allow loading of component via Loader.sourceComponent
2 | Object {
3 | property Item delegate; ///< delegate - template object
4 | }
5 |
--------------------------------------------------------------------------------
/core/Connection.qml:
--------------------------------------------------------------------------------
1 | /// Describes generalized connections to signals. However it's preferrable to use extended syntax, e.g. `myButton.onClicked: { }` instead of dynamic Connection. This class is useful if you want to switch your targets on the fly
2 | Object {
3 | property Object target; ///< target object to connect to
4 |
5 | constructor: {
6 | this._declaredOnConnections = []
7 | this._declaredOnChangedConnections = []
8 | }
9 |
10 | /// @private
11 | function _disconnect() {
12 | this.removeAllOn()
13 | this.removeAllOnChanged()
14 | }
15 |
16 | /// @private
17 | function _reconnect() {
18 | this._disconnect()
19 |
20 | var target = this.target
21 | if (!target)
22 | return
23 |
24 | if (!(target instanceof $core.CoreObject))
25 | throw new Error("You can only assign qml objects to target, got: " + target + " of type " + typeof target + " to " + this.getComponentPath())
26 |
27 | //reconnect onTargetChanged
28 | this.connectOnChanged(this, 'target', this._scheduleReconnect.bind(this)) //restore target connection
29 |
30 |
31 | var ons = this._declaredOnConnections
32 | for(var i = 0, n = ons.length; i < n; i += 2) {
33 | this.connectOn(this.target, ons[i], ons[i + 1])
34 | }
35 | ons = this._declaredOnChangedConnections
36 | for(var i = 0, n = ons.length; i < n; i += 2) {
37 | this.connectOnChanged(this.target, ons[i], ons[i + 1])
38 | }
39 | }
40 |
41 | /// @private
42 | function _scheduleReconnect() {
43 | this._context.delayedAction('reconnect', this, this._reconnect)
44 | }
45 |
46 | /// @private
47 | function on (name, callback) {
48 | if (name === 'target')
49 | return
50 |
51 | if (name === '')
52 | throw new Error('empty listener name')
53 |
54 | this._declaredOnConnections.push(name, callback)
55 | if (this.target) {
56 | this.connectOn(this.target, name, callback)
57 | }
58 | }
59 |
60 | /// @private
61 | function onChanged (name, callback) {
62 | if (name === 'target')
63 | return
64 |
65 | if (name === '')
66 | throw new Error('empty listener name')
67 |
68 | this._declaredOnChangedConnections.push(name, callback)
69 | if (this.target)
70 | this.connectOnChanged(this.target, name, callback)
71 | }
72 |
73 | onTargetChanged,
74 | onCompleted:
75 | { this._scheduleReconnect(); }
76 | }
77 |
--------------------------------------------------------------------------------
/core/ContentMargin.qml:
--------------------------------------------------------------------------------
1 | Object {
2 | property int top;
3 | property int left;
4 | property int right;
5 | property int bottom;
6 | }
7 |
--------------------------------------------------------------------------------
/core/ContextMenuMixin.qml:
--------------------------------------------------------------------------------
1 | /// This mixin provides mouse click event detecting
2 | BaseMixin {
3 | ///@private
4 | constructor: {
5 | this._bindClick(this.enabled)
6 | }
7 |
8 | ///@private
9 | function _bindClick(value) {
10 | if (value && !this._cmClickBinder) {
11 | this._cmClickBinder = new $core.EventBinder(this.element)
12 | this._cmClickBinder.on('contextmenu', $core.createSignalForwarder(this.parent, 'contextMenu').bind(this))
13 | }
14 | if (this._cmClickBinder)
15 | this._cmClickBinder.enable(value)
16 | }
17 |
18 | ///@private
19 | onEnabledChanged: { this._bindClick(value) }
20 | }
21 |
--------------------------------------------------------------------------------
/core/Device.qml:
--------------------------------------------------------------------------------
1 | /// Object which contains device specific information like model name. MAC address etc.
2 | Object {
3 | signal propertyUpdated; ///< this signal is emited every time when one of the property was changed
4 | property string macAddress; ///< device MAC address if its available for current platform
5 | property string modelName; ///< device model name
6 | property string deviceId; ///< unique device id or random generated id if its not supported
7 | property string firmware; ///< firmware version
8 | property string language; ///< device langgcode
9 | property string country; ///< device country code
10 | property string sdk; ///< SDK version
11 | property string ip; ///< device IP address if its available
12 | property string runtime; ///< pureqml specific runtime provider, cordova/native
13 |
14 | property bool standByMode; ///< Stand by mode flag (if its supported)
15 | property bool supportingUhd; ///< UHD (4K) supporting flag
16 | property bool supportingHdr; ///< HDR supporting flag
17 | property bool supporting3d; ///< 3D Video supporting flag
18 |
19 | constructor: {
20 | var backend = $core.__deviceBackend
21 | if (!backend)
22 | throw new Error('no backend found')
23 |
24 | this.impl = backend().createDevice(this)
25 | }
26 |
27 | onSdkChanged,
28 | onDeviceIdChanged,
29 | onFirmwareChanged,
30 | onMacAddessChanged,
31 | onModelNameChanged,
32 | onSupporting3dChanged,
33 | onSupportingUhdChanged: { this.propertyUpdated() }
34 |
35 | onStandByModeChanged: {
36 | if (!this.impl) {
37 | throw new Error('toggleStandByMode: no backend found')
38 | return
39 | }
40 |
41 | this.impl.toggleStandByMode()
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/core/Effects.qml:
--------------------------------------------------------------------------------
1 | ///provides various visual effects
2 | Object {
3 | property real blur; ///< applies a blur effect to the image
4 | property real grayscale; ///< converts the image to grayscale
5 | property real sepia; ///< converts the image to sepia
6 | property real brightness; ///< adjusts the brightness of the image
7 | property real contrast; ///< adjusts the contrast of the image
8 | property real hueRotate; ///< applies a hue rotation on the image. The value defines the number of degrees around the color circle the image samples will be adjusted
9 | property real invert; ///< inverts the samples in the image
10 | property real saturate; ///< saturates the image
11 | property lazy shadow : Shadow { } ///< object property for the shadow adjusting
12 |
13 | /// @private
14 | function _addStyle(array, property, style, units) {
15 | var value = this[property]
16 | if (value)
17 | array.push((style || property) + '(' + value + (units || '') + ') ')
18 | }
19 |
20 | /// @private
21 | function _getFilterStyle() {
22 | var style = []
23 | this._addStyle(style, 'blur', 'blur', 'px')
24 | this._addStyle(style, 'grayscale')
25 | this._addStyle(style, 'sepia')
26 | this._addStyle(style, 'brightness')
27 | this._addStyle(style, 'contrast')
28 | this._addStyle(style, 'hueRotate', 'hue-rotate', 'deg')
29 | this._addStyle(style, 'invert')
30 | this._addStyle(style, 'saturate')
31 | return style
32 | }
33 |
34 | /// @private
35 | function _updateStyle(updateShadow) {
36 | var filterStyle = this._getFilterStyle().join('')
37 | var parent = this.parent
38 | var style = {}
39 |
40 | //chromium bug
41 | //https://github.com/Modernizr/Modernizr/issues/981
42 | style['-webkit-filter'] = filterStyle
43 | style['filter'] = filterStyle
44 |
45 | if (this.shadow && (!this.shadow._empty() || updateShadow))
46 | style['box-shadow'] = this.shadow._getFilterStyle()
47 |
48 | parent.style(style)
49 | }
50 |
51 | onBlurChanged, onGrayscaleChanged,
52 | onSepiaChanged, onBrightnessChanged,
53 | onContrastChanged, onHueRotateChanged,
54 | onInvertChanged, onSaturateChanged: { this._updateStyle() }
55 | }
56 |
--------------------------------------------------------------------------------
/core/EventEmitter.qml:
--------------------------------------------------------------------------------
1 | ///@private
2 | CoreObject {
3 | constructor: {
4 | this._eventHandlers = {}
5 | this._onConnections = []
6 | }
7 |
8 | /// @private removes all on(signal) connections
9 | function removeAllOn() {
10 | var connections = this._onConnections
11 | for(var i = 0, n = connections.length; i < n; i += 3)
12 | connections[i].removeListener(connections[i + 1], connections[i + 2])
13 | this._onConnections = []
14 | }
15 |
16 | function discard() {
17 | this.removeAllOn()
18 | for(var name in this._eventHandlers)
19 | this.removeAllListeners(name)
20 | }
21 |
22 | function on (name, callback) {
23 | if (name === '')
24 | throw new Error('empty listener name')
25 |
26 | var storage = this._eventHandlers
27 | var handlers = storage[name]
28 | if (handlers !== undefined)
29 | handlers.push(callback)
30 | else {
31 | storage[name] = [callback]
32 | }
33 | }
34 |
35 | function connectOn(target, name, callback) {
36 | target.on(name, callback)
37 | this._onConnections.push(target, name, callback)
38 | }
39 |
40 | function emit (name) {
41 | if (name === '')
42 | throw new Error('empty listener name')
43 |
44 | var proto_callback = this['__on__' + name]
45 | var handlers = this._eventHandlers[name]
46 |
47 | if (proto_callback === undefined && handlers === undefined)
48 | return
49 |
50 | COPY_ARGS(args, 1)
51 |
52 | var invoker = $core.safeCall(
53 | this, args,
54 | function(ex) { log("event/signal " + name + " handler failed:", ex, ex.stack) }
55 | )
56 |
57 | if (proto_callback !== undefined)
58 | proto_callback.forEach(invoker)
59 |
60 | if (handlers !== undefined)
61 | handlers.forEach(invoker)
62 | }
63 |
64 | function emitWithArgs (name, args) {
65 | if (name === '')
66 | throw new Error('empty listener name')
67 |
68 | var proto_callback = this['__on__' + name]
69 | var handlers = this._eventHandlers[name]
70 |
71 | if (proto_callback === undefined && handlers === undefined)
72 | return
73 |
74 | var invoker = $core.safeCall(
75 | this, args,
76 | function(ex) { log("event/signal " + name + " handler failed:", ex, ex.stack) }
77 | )
78 |
79 | if (proto_callback !== undefined)
80 | proto_callback.forEach(invoker)
81 |
82 | if (handlers !== undefined)
83 | handlers.forEach(invoker)
84 | }
85 |
86 | function removeAllListeners(name) {
87 | delete this._eventHandlers[name]
88 | }
89 |
90 | function removeListener (name, callback) {
91 | if (!(name in this._eventHandlers) || callback === undefined || callback === null || name === '') {
92 | if ($manifest$trace$listeners)
93 | log('invalid removeListener(' + name + ', ' + callback + ') invocation', new Error().stack)
94 | return
95 | }
96 |
97 | var handlers = this._eventHandlers[name]
98 | var idx = handlers.indexOf(callback)
99 | if (idx >= 0)
100 | handlers.splice(idx, 1)
101 | else if ($manifest$trace$listeners)
102 | log('failed to remove listener for', name, 'from', this)
103 |
104 | if (!handlers.length)
105 | this.removeAllListeners(name)
106 | }
107 |
108 | }
109 |
--------------------------------------------------------------------------------
/core/Font.qml:
--------------------------------------------------------------------------------
1 | /// adjusts text font properties
2 | Object {
3 | property string family: manifest.style.font.family; ///< font family
4 | property bool italic; ///< applies italic style
5 | property bool bold; ///< applies bold style
6 | property bool underline; ///< applies underline style
7 | property bool overline; ///< applies overline style
8 | property bool strike; ///< line throw text flag
9 | property bool strikeout; ///< line throw text flag for compatibility with QML
10 | property real letterSpacing; ///< spacing between letters
11 | property real wordSpacing; ///< spacing between words
12 | property int pixelSize: manifest.style.font.pixelSize; ///< font size in pixels
13 | property int pointSize; ///< font size in points
14 | property real lineHeight: manifest.style.font.lineHeight; ///< font line height in font heights
15 | property int weight; ///< font weight value
16 | property enum capitalization { MixedCase, AllUppercase, AllLowercase, SmallCaps, Capitalize };
17 |
18 | ///@private
19 | function _updateTextDecoration() {
20 | var decoration = (this.underline ? ' underline' : '')
21 | + (this.overline ? ' overline' : '')
22 | + (this.strike || this.strikeout ? ' line-through' : '')
23 | this.parent.style('text-decoration', decoration)
24 | this.parent._updateSize()
25 | }
26 |
27 | onFamilyChanged: { this.parent.style('font-family', value); this.parent._updateSize() }
28 | onPointSizeChanged: { if (value > 0) this.pixelSize = 0; this.parent.style('font-size', value > 0? value + 'pt': ''); this.parent._updateSize() }
29 | onPixelSizeChanged: { if (value > 0) this.pointSize = 0; this.parent.style('font-size', value > 0? value + 'px': ''); this.parent._updateSize() }
30 | onItalicChanged: { this.parent.style('font-style', value? 'italic': 'normal'); this.parent._updateSize() }
31 | onBoldChanged: { this.parent.style('font-weight', value? 'bold': 'normal'); this.parent._updateSize() }
32 | onUnderlineChanged: { this._updateTextDecoration() }
33 | onOverlineChanged: { this._updateTextDecoration() }
34 | onStrikeChanged,
35 | onStrikeoutChanged: { this._updateTextDecoration() }
36 | onLineHeightChanged: { this.parent.style('line-height', value); this.parent._updateSize() }
37 | onWeightChanged: { this.parent.style('font-weight', value); this.parent._updateSize() }
38 | onLetterSpacingChanged: { this.parent.style('letter-spacing', value + "px"); this.parent._updateSize() }
39 | onWordSpacingChanged: { this.parent.style('word-spacing', value + "px"); this.parent._updateSize() }
40 | onCapitalizationChanged: {
41 | this.parent.style('text-transform', 'none');
42 | this.parent.style('font-variant', 'normal');
43 | switch(value) {
44 | case this.AllUppercase: this.parent.style('text-transform', 'uppercase'); break
45 | case this.AllLowercase: this.parent.style('text-transform', 'lowercase'); break
46 | case this.SmallCaps: this.parent.style('font-variant', 'small-caps'); break
47 | case this.Capitalize: this.parent.style('text-transform', 'capitalize'); break
48 | }
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/core/GamepadManager.qml:
--------------------------------------------------------------------------------
1 | ///gamepad manager item holds Gamepad items and provide common API
2 | Item {
3 | signal connected; ///< emitted when any gamepad is connected
4 | signal disconnected; ///< emitted when gamepad is disconnected
5 |
6 | property variant _gamepads; ///< @private
7 | property int count: 0; ///< count of the all connected gamepad devices
8 | property int gamepadChildrensCount: 0; ///< count of Gamepad instances inside scope
9 | property int gamepadPollingInterval: 1000; ///< startup delay before gamepads polling because there is no gamepad events
10 | property int eventPollingInterval: 8; ///< gamepad event polling timer interval default value is 8ms (for 120fps) because there is no gamepad events
11 |
12 | Timer {
13 | id: startupTimer;
14 | interval: parent.gamepadPollingInterval;
15 | repeat: false;
16 | triggeredOnStart: true;
17 |
18 | onTriggered: { this.parent.pollGamepads() }
19 | }
20 |
21 | Timer {
22 | interval: parent.eventPollingInterval;
23 | repeat: true;
24 | running: parent.gamepadChildrensCount;
25 | triggeredOnStart: true;
26 |
27 | onTriggered: { this.parent.gpButtonCheckLoop() }
28 | }
29 |
30 | /// @private
31 | pollGamepads: {
32 | clearInterval(this._gpPollInterval)
33 | var gamepads = navigator.getGamepads ? navigator.getGamepads() : (navigator.webkitGetGamepads ? navigator.webkitGetGamepads : []);
34 | for (var i = 0; i < gamepads.length; ++i) {
35 | var gamepad = gamepads[i]
36 | if (gamepad)
37 | this.gamepadConnectedHandler({ 'gamepad': gamepad })
38 | }
39 | }
40 |
41 | /// @private
42 | gpButtonCheckLoop: {
43 | clearInterval(this._gpButtonsPollInterval);
44 | var gamepads = navigator.getGamepads ? navigator.getGamepads() : (navigator.webkitGetGamepads ? navigator.webkitGetGamepads : []);
45 | for (var i in gamepads) {
46 | if (!gamepads[i] || !gamepads[i].buttons)
47 | continue
48 |
49 | var gp = gamepads[i]
50 | var gpItem
51 |
52 | for (var i = 0; i < this.children.length; ++i) {
53 | var c = this.children[i]
54 | if (c instanceof $core.Gamepad && c.connected && c.index === gp.index) {
55 | gpItem = c
56 | break
57 | }
58 | }
59 |
60 | if (!gp || !gpItem)
61 | continue
62 |
63 | gpItem.poll(gp)
64 | }
65 | }
66 |
67 | /// @private
68 | gamepadConnectedHandler(event): {
69 | log('connected', event.gamepad.id)
70 | this.connected(event.gamepad)
71 |
72 | if (!this._gamepads)
73 | this._gamepads = {}
74 | this._gamepads[event.gamepad.index] = event.gamepad
75 | ++this.count
76 |
77 | if (!$core.Gamepad) {
78 | log("No 'Gamepad' instance found, add at least one 'Gamepad' item inside 'GamepadManager' scope.")
79 | return
80 | }
81 |
82 | var vendorRegExp = /vendor.*?\d{1,4}/g
83 | var productRegExp = /product.*?\d{1,4}/g
84 | var digits = /\d{1,4}/g
85 |
86 | var idStr = event.gamepad.id.toLowerCase()
87 | var match = vendorRegExp.exec(idStr)
88 | match = digits.exec(match)
89 | var vendorId
90 | if (match && match.length)
91 | vendorId = match[0]
92 |
93 | match = productRegExp.exec(idStr)
94 | match = digits.exec(match)
95 | var productId
96 | if (match && match.length)
97 | productId = match[0]
98 |
99 | var children = this.children
100 | var g = event.gamepad
101 | for (var i = 0; i < children.length; ++i) {
102 | var c = children[i]
103 | if (c instanceof $core.Gamepad && !c.connected) {
104 | c.index = g.index
105 | c.connected = true
106 | c.deviceInfo = g.id
107 | c.buttonsCount = g.buttons.length
108 | c.axesCount = g.axes.length
109 | if (vendorId)
110 | c.vendorId = vendorId
111 | if (productId)
112 | c.productId = productId
113 | c.standartMapping = g.mapping === "standard"
114 | ++this.gamepadChildrensCount
115 | break
116 | }
117 | }
118 | }
119 |
120 | /// @private
121 | gamepadDisconnectedHandler(event): {
122 | this.disconnected(event.gamepad)
123 | delete this._gamepads[event.gamepad.index]
124 | --this.count
125 |
126 | if (!$core.Gamepad) {
127 | log("No 'Gamepad' instance found, add at least one 'Gamepad' item inside 'GamepadManager' scope.")
128 | return
129 | }
130 |
131 | var g = event.gamepad
132 | var children = this.children
133 |
134 | for (var i = 0; i < children.length; ++i) {
135 | var c = children[i]
136 | if (c instanceof $core.Gamepad && c.index === g.index) {
137 | c.index = -1
138 | c.connected = false
139 | c.deviceInfo = ""
140 | c.buttonsCount = 0
141 | c.axesCount = 0
142 | --this.gamepadChildrensCount
143 | break
144 | }
145 | }
146 | }
147 |
148 | /// @private
149 | onCompleted: {
150 | this._gpButtonsPollInterval = {}
151 | this._gpPollInterval = {}
152 |
153 | startupTimer.restart()
154 |
155 | var ctx = this._context
156 | ctx.window.on('gamepadconnected', this.gamepadConnectedHandler.bind(this))
157 | ctx.window.on('gamepaddisconnected', this.gamepadDisconnectedHandler.bind(this))
158 | }
159 | }
160 |
--------------------------------------------------------------------------------
/core/Gradient.qml:
--------------------------------------------------------------------------------
1 | /// gradient filled area, just place GradientStop in its scope
2 | Object {
3 | property enum orientation { Vertical, Horizontal, BottomRight, TopRight, Custom }; ///< gradient direction enumaration
4 | property real angle; ///< angle for custom orientated gradient
5 | property enum type { Linear, Conical }: Linear; /// < Linear by default to preserve backward compatibility
6 |
7 | ///@private
8 | constructor: {
9 | this.stops = []
10 | }
11 |
12 | ///@private
13 | function addChild(child) {
14 | $core.Object.prototype.addChild.apply(this, arguments)
15 | if (child instanceof $core.GradientStop) {
16 | this.stops.push(child)
17 | this.stops.sort(function(a, b) { return a.position > b.position; })
18 | this._updateStyle()
19 | }
20 | }
21 |
22 | ///@private
23 | function _updateStyle() {
24 | var decl = this._getDeclaration()
25 | if (decl)
26 | this.parent.style({ 'background-color': '', 'background': decl })
27 | }
28 |
29 | ///@private
30 | function _getDeclaration() {
31 | var stops = this.stops
32 | var n = stops.length
33 | if (n < 2)
34 | return
35 |
36 | var orientation
37 | if (this.type == this.Linear) {
38 | switch(this.orientation) {
39 | default:
40 | case this.Vertical: orientation = 'to bottom'; break
41 | case this.Horizontal: orientation = 'to left'; break
42 | case this.BottomRight: orientation = 'to bottom right'; break
43 | case this.TopRight: orientation = 'to top right'; break
44 | case this.Custom: orientation = this.angle + 'deg'; break
45 | }
46 | }
47 | else if (this.type == this.Conical) {
48 | orientation = this.angle + 'deg at 50% 50%'; // TODO parameterize the center
49 | }
50 |
51 | var grad = new $core.gradient.Gradient(orientation, this.type)
52 |
53 | for(var i = 0; i < n; ++i) {
54 | var stop = stops[i]
55 | grad.add(stop._getDeclaration())
56 | }
57 |
58 | return grad
59 | }
60 |
61 | ///@private
62 | onCompleted: {
63 | this._updateStyle()
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/core/GradientStop.qml:
--------------------------------------------------------------------------------
1 | /// this object is used for customizing Gradient
2 | Object {
3 | property real position; ///< relative position of the stop must be in range [0:1]
4 | property color color; ///< color of this stop
5 |
6 | ///@private
7 | onPositionChanged, onColorChanged: {
8 | this.parent._updateStyle()
9 | }
10 |
11 | ///@private
12 | function _getDeclaration() {
13 | return new $core.gradient.GradientStop(this.color, this.position)
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/core/HoverMixin.qml:
--------------------------------------------------------------------------------
1 | /// this mixin provides mouse hover events handling
2 | BaseMouseMixin {
3 | property bool value; ///< is 'true' if item if hovered, 'false' otherwise
4 |
5 | ///@private
6 | constructor: {
7 | this.element = this.parent.element;
8 | this.parent.style('cursor', this.cursor)
9 | this._bindHover(this.enabled)
10 | if (!exports.hasProperty(parent, "hovered"))
11 | exports.addProperty(parent, "bool", "hovered", false)
12 | }
13 |
14 | ///@private
15 | function _bindHover(value) {
16 | if (value && !this._hmHoverBinder) {
17 | this._hmHoverBinder = new $core.EventBinder(this.parent.element)
18 |
19 | if (this._context.backend.capabilities.mouseEnterLeaveSupported) {
20 | this._hmHoverBinder.on('mouseenter', function() { this.value = this._trueUnlessTouchEvent() }.bind(this))
21 | this._hmHoverBinder.on('mouseleave', function() { this.value = false }.bind(this))
22 | } else {
23 | this._hmHoverBinder.on('mouseover', function() { this.value = this._trueUnlessTouchEvent() }.bind(this))
24 | this._hmHoverBinder.on('mouseout', function() { this.value = false }.bind(this))
25 | }
26 | this._hmHoverBinder.on('touchstart', this._setTouchEvent.bind(this))
27 | this._hmHoverBinder.on('mouseup', this._resetTouchEvent.bind(this))
28 | }
29 | if (this._hmHoverBinder)
30 | this._hmHoverBinder.enable(value)
31 | }
32 |
33 | onValueChanged: { this.parent.hovered = value }
34 | onEnabledChanged: { this._bindHover(value) }
35 | }
36 |
--------------------------------------------------------------------------------
/core/Layout.qml:
--------------------------------------------------------------------------------
1 | /// base layout component.
2 | BaseLayout {
3 | width: contentWidth; ///< inner content width
4 | height: contentHeight; ///< inner content height
5 | handleNavigationKeys: true; ///< handle navigation keys, move focus
6 | keyNavigationWraps: true; ///< key navigation wraps from first to last and vise versa
7 |
8 | ///move focus to the next child
9 | focusNextChild: {
10 | var idx = 0;
11 | var children = this.children
12 | if (this.focusedChild)
13 | idx = children.indexOf(this.focusedChild)
14 |
15 | for (var i = idx + 1; i < children.length; ++i) {
16 | if (children[i]._tryFocus()) {
17 | this.currentIndex = i
18 | this.focusChild(this.children[i])
19 | return true
20 | }
21 | }
22 |
23 | if (!this.keyNavigationWraps)
24 | return false
25 |
26 | for (var i = 0; i <= idx; ++i) {
27 | if (children[i]._tryFocus()) {
28 | this.currentIndex = i
29 | this.focusChild(this.children[i])
30 | return true
31 | }
32 | }
33 |
34 | return false
35 | }
36 |
37 | ///move focus to the previous child
38 | focusPrevChild: {
39 | var idx = 0;
40 | var children = this.children
41 | if (this.focusedChild)
42 | idx = children.indexOf(this.focusedChild)
43 |
44 | for (var i = idx - 1; i >= 0; --i) {
45 | if (children[i]._tryFocus()) {
46 | this.currentIndex = i
47 | this.focusChild(this.children[i])
48 | return true
49 | }
50 | }
51 |
52 | if (!this.keyNavigationWraps)
53 | return false
54 |
55 | var last = children.length - 1
56 | for (var i = last; i >= idx; --i) {
57 | if (children[i]._tryFocus()) {
58 | this.currentIndex = i
59 | this.focusChild(this.children[i])
60 | return true
61 | }
62 | }
63 |
64 | return false
65 |
66 | }
67 |
68 | onCurrentIndexChanged: {
69 | if (value >= 0 && value < this.children.length)
70 | this.focusChild(this.children[value])
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/core/Loader.qml:
--------------------------------------------------------------------------------
1 | /// object that helps loading components dynamically
2 | Item {
3 | signal loaded; ///< signals loaded component (first argument)
4 | signal itemCompleted; ///< fires after item onCompleted has been fired and item is fully constructed
5 | property string source; ///< component's URL
6 | property Component sourceComponent; ///< loads delegate from component.
7 | property Object item; ///< item for storing requested component
8 | property bool trace; ///< log loading objects
9 |
10 | ///@private
11 | function discardItem() {
12 | this._itemCompleted = false
13 | var item = this.item
14 | if (item) {
15 | item.discard()
16 | this.item = null
17 | }
18 | }
19 |
20 | ///@private
21 | function discard() {
22 | this.discardItem()
23 | $core.Item.prototype.discard.call(this)
24 | }
25 |
26 | ///@internal
27 | onSourceChanged: {
28 | this.discardItem()
29 | this._load()
30 | }
31 | onSourceComponentChanged: {
32 | this.discardItem()
33 | this._load()
34 | }
35 |
36 | function _loadSource() {
37 | var source = this.source
38 | if (this.trace)
39 | log('loading ' + source + '…')
40 | var path = source.split('.')
41 | var ctor = _globals
42 | while(path.length) {
43 | var ns = path.shift()
44 | ctor = ctor[ns]
45 | if (ctor === undefined)
46 | throw new Error('unknown component used: ' + source)
47 | }
48 | return new ctor(this)
49 | }
50 |
51 | ///@internal
52 | function _load() {
53 | var item
54 | if (this.source) {
55 | item = this._loadSource()
56 | $core.core.createObject(item)
57 | } else if (this.sourceComponent) {
58 | if (!(this.sourceComponent instanceof $core.Component))
59 | throw new Error("sourceComponent assigned to Loader " + this.getComponentPath() + " is not an instance of Component")
60 | if (this.trace)
61 | log('loading component ' + this.sourceComponent.getComponentPath())
62 | var row = this.parent._get('model', true)
63 | item = this.sourceComponent.delegate(this, row? row: {})
64 | this._updateVisibilityForChild(item, this.recursiveVisible)
65 | this._tryFocus()
66 | } else
67 | return
68 |
69 | this.item = item
70 |
71 | this.loaded(item)
72 |
73 | var hasOnCompleted = item.__complete !== $core.CoreObject.prototype.__complete
74 | if (hasOnCompleted) {
75 | // Schedule dummy object which calls itemCompleted(item)
76 | // It's guaranteed to execute after possibly delayed item.onComplete handler.
77 | var complete = function() {
78 | //check that item hasn't been changed.
79 | if (this.item === item) {
80 | this._itemCompleted = true
81 | this.itemCompleted(item)
82 | }
83 | }.bind(this)
84 |
85 | this._context.__onCompleted({
86 | __complete: complete
87 | })
88 | } else {
89 | this._itemCompleted = true
90 | this.itemCompleted(item)
91 | }
92 | }
93 |
94 | onRecursiveVisibleChanged: {
95 | if (this.item)
96 | this._updateVisibilityForChild(this.item, value)
97 | }
98 |
99 | /// @private
100 | function on (name, callback) {
101 | $core.Item.prototype.on.apply(this, arguments)
102 | if (name === 'loaded') {
103 | if (this.item)
104 | callback(this.item)
105 | }
106 | if (name === 'itemCompleted') {
107 | if (this._itemCompleted)
108 | callback(this.item)
109 | }
110 | }
111 |
112 | ///@internal
113 | onCompleted: {
114 | if (!this.item && (this.source || this.sourceComponent))
115 | this._load()
116 | }
117 | }
118 |
--------------------------------------------------------------------------------
/core/LocalStorage.qml:
--------------------------------------------------------------------------------
1 | /// simple proxy to underlying storage
2 | Object {
3 | constructor: {
4 | var backend = $core.__localStorageBackend
5 | this.impl = backend().createLocalStorage(this)
6 | }
7 |
8 | ///@private
9 | _checkNameValid(name): {
10 | if (!name) throw new Error("empty name")
11 | }
12 |
13 | ///@private
14 | _ensureCallback(cb, name): {
15 | return cb || function(val) { log("ignore value of", name, "gotten from storage:", val) }
16 | }
17 |
18 | ///@private
19 | _ensureErrCallback(cb): {
20 | return cb || function(err) { log(err.message) }
21 | }
22 |
23 | /**
24 | * Return stored item by name
25 | * @param {string} name - stored item name
26 | * @param {function} callback - callback to return value
27 | * @param {function} error - callback to report non-existing value or some kind of error
28 | */
29 | get(name, callback, error): {
30 | this._checkNameValid(name)
31 | this.impl.get(name, this._ensureCallback(callback, name), this._ensureErrCallback(error), this)
32 | }
33 |
34 | /**
35 | * Return stored item by name or default value if not exists
36 | * @param {string} name - stored item name
37 | * @param {function} callback - callback to return value
38 | * @param {Object} defaultValue - default value
39 | */
40 | getOrDefault(name, callback, defaultValue): {
41 | this._checkNameValid(name)
42 | callback = this._ensureCallback(callback, name)
43 | this.impl.get(name, callback, function() { callback(defaultValue) }, this)
44 | }
45 |
46 | /**
47 | * Save named item
48 | * @param {string} name - item name
49 | * @param {string} value - item value
50 | * @param {function} error - callback to report error
51 | */
52 | set(name, value, error): {
53 | this._checkNameValid(name)
54 | this.impl.set(name, value, this._ensureErrCallback(error), this)
55 | }
56 |
57 | /**
58 | * Remove item
59 | * @param {string} name - item name
60 | * @param {function} error - callback to report error
61 | */
62 | erase(name, error): {
63 | this._checkNameValid(name)
64 | this.impl.erase(name, this._ensureErrCallback(error), this)
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/core/Location.qml:
--------------------------------------------------------------------------------
1 | /// Window location object
2 | Object {
3 | property string hash; ///< contains current hash value (after '#' charachter)
4 | property string host; ///< current host with port number
5 | property string href; ///< whole current URL
6 | property string port; ///< current port number
7 | property string origin; ///< current protocol, hostname and port number of a URL
8 | property string hostname; ///< current host name
9 | property string pathname; ///< path name of the current URL
10 | property string protocol; ///< current protocol
11 | property string search; ///< query string of the URL
12 | property Object state; ///< current history state
13 |
14 | ///@private
15 | constructor: {
16 | var backend = $core.__locationBackend
17 | if (!backend)
18 | throw new Error('no backend found')
19 | this.impl = backend().createLocation(this)
20 | }
21 |
22 | pushState(state, title, url): {
23 | this.impl.pushState(state, title, url)
24 | }
25 |
26 | changeHref(href): {
27 | this.impl.changeHref(href)
28 | }
29 |
30 | function reload() {
31 | this.impl.reload()
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/core/Model.qml:
--------------------------------------------------------------------------------
1 | Object {
2 | signal reset; ///< model reset signal
3 | signal rowsInserted; ///< rows inserted signal
4 | signal rowsChanged; ///< rows changed signal
5 | signal rowsRemoved; ///< rows removed signal
6 |
7 | property int count; ///< model rows count. Please note that you can't directly/indirectly modify model from onChanged handler. Use view.onCountChanged instead
8 |
9 | /// @private
10 | function attachTo(object) {
11 | if (object._modelAttached)
12 | object._modelAttached.detachFrom(object)
13 | if (!object._modelReset)
14 | object._modelReset = object._onReset.bind(object)
15 | if (!object._modelRowsInserted)
16 | object._modelRowsInserted = object._onRowsInserted.bind(object)
17 | if (!object._modelRowsChanged)
18 | object._modelRowsChanged = object._onRowsChanged.bind(object)
19 | if (!object._modelRowsRemoved)
20 | object._modelRowsRemoved = object._onRowsRemoved.bind(object)
21 |
22 | var Model = $core.Model
23 | var model = this
24 | var modelType = typeof model
25 | if ((Model !== undefined) && (model instanceof Model)) {
26 | } else if (Array.isArray(model)) {
27 | model = new $core.model.ArrayModelWrapper(model)
28 | } else if (modelType === 'number') {
29 | var data = []
30 | for(var i = 0; i < model; ++i)
31 | data.push({})
32 | model = new $core.model.ArrayModelWrapper(data)
33 | } else
34 | throw new Error("unknown value of type '" + (typeof model) + "', attached to model property: " + model + ((modelType === 'object') && ('componentName' in model)? ', component name: ' + model.componentName: ''))
35 |
36 | model.on('reset', object._modelReset)
37 | model.on('rowsInserted', object._modelRowsInserted)
38 | model.on('rowsChanged', object._modelRowsChanged)
39 | model.on('rowsRemoved', object._modelRowsRemoved)
40 |
41 | object._modelAttached = model
42 | object._onReset()
43 | }
44 |
45 | /// @private
46 | function detachFrom(object) {
47 | var model = object._modelAttached
48 | if (!model)
49 | return
50 |
51 | object._modelAttached = null
52 |
53 | model.removeListener('reset', object._modelReset)
54 | model.removeListener('rowsInserted', object._modelRowsInserted)
55 | model.removeListener('rowsChanged', object._modelRowsChanged)
56 | model.removeListener('rowsRemoved', object._modelRowsRemoved)
57 |
58 | }
59 |
60 | }
61 |
--------------------------------------------------------------------------------
/core/MouseMoveMixin.qml:
--------------------------------------------------------------------------------
1 | /// this mixin provides mouse press events handling
2 | BaseMouseMixin {
3 | property int mouseX;
4 | property int mouseY;
5 | property int clientX;
6 | property int clientY;
7 | property int screenX;
8 | property int screenY;
9 | signal mouseMove;
10 |
11 | ///@private
12 | constructor: {
13 | this._bindMove(this.enabled)
14 | }
15 |
16 | /// @private
17 | function _updatePosition(event) {
18 | var parent = this.parent
19 | var touchEvent = event.type === 'touchmove'
20 | var x, y
21 | if (touchEvent) {
22 | var touch = event.touches[0]
23 | this.screenX = touch.screenX;
24 | this.screenY = touch.screenY;
25 | this.clientX = touch.clientX;
26 | this.clientY = touch.clientY;
27 | var screenPos = this.parent.toScreen()
28 | x = touch.clientX - screenPos[0]
29 | y = touch.clientY - screenPos[1]
30 | } else {
31 | this.screenX = event.screenX;
32 | this.screenY = event.screenY;
33 | this.clientX = event.clientX;
34 | this.clientY = event.clientY;
35 | x = event.offsetX
36 | y = event.offsetY
37 | }
38 | if (x >= 0 && y >= 0 && x < parent.width && y < parent.height) {
39 | this.mouseX = x
40 | this.mouseY = y
41 | this.mouseMove(x, y, event)
42 | return true
43 | }
44 | else
45 | return false
46 | }
47 |
48 | /// @private
49 | function _bindMove(value) {
50 | if (value && !this._mouseMoveBinder) {
51 | this._mouseMoveBinder = new $core.EventBinder(this.element)
52 | var handler = function(event) {
53 | if (this._updatePosition(event) && event.type !== 'touchmove')
54 | $core.callMethod(event, 'preventDefault')
55 | }.bind(this)
56 | this._mouseMoveBinder.on('mousemove', handler)
57 | this._mouseMoveBinder.on('touchmove', handler)
58 | }
59 | if (this._mouseMoveBinder)
60 | this._mouseMoveBinder.enable(value)
61 | }
62 |
63 | onEnabledChanged: {
64 | this._bindMove(value)
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/core/MousePressMixin.qml:
--------------------------------------------------------------------------------
1 | /// this mixin provides mouse press events handling
2 | BaseMouseMixin {
3 | property bool pressed; ///< true if any buttons pressed
4 |
5 | ///@private
6 | constructor: {
7 | this._bindPress(this.enabled)
8 | }
9 |
10 | /// @private
11 | function _bindPress(value) {
12 | if (value && !this._mpmPressBinder) {
13 | this._mpmPressBinder = new $core.EventBinder(this.element)
14 | this._mpmPressBinder.on('mousedown', function() { this.pressed = true }.bind(this))
15 | this._mpmPressBinder.on('mouseup', function() { this.pressed = false }.bind(this))
16 | }
17 | if (this._mpmPressBinder)
18 | this._mpmPressBinder.enable(value)
19 | }
20 |
21 | /// @private
22 | onEnabledChanged: {
23 | this._bindPress(value)
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/core/PageStack.qml:
--------------------------------------------------------------------------------
1 | ///layout for displaying one of its children at the time
2 | Layout {
3 | property int currentIndex: 0; ///< index of displaying child
4 | property int count: 0; ///< childrens count
5 | clip: true; ///@private
6 |
7 | /// @private
8 | onCurrentIndexChanged: {
9 | if (this.currentIndex < 0)
10 | this.currentIndex = 0;
11 | else if (this.children.length > 0 && this.currentIndex >= this.children.length)
12 | this.currentIndex = this.children.length - 1;
13 |
14 | this._scheduleLayout()
15 | }
16 |
17 | /// @private
18 | onActiveFocusChanged: {
19 | if (value && this.count)
20 | this.children[this.currentIndex].setFocus()
21 | }
22 |
23 | /// @private
24 | function _layout() {
25 | this.count = this.children.length;
26 | if (this.trace)
27 | log('laying out ' + this.count + ' children in ' + this.width + 'x' + this.height)
28 |
29 | for (var i = 0; i < this.count; ++i)
30 | this.children[i].visibleInView = (i === this.currentIndex);
31 |
32 | var c = this.children[this.currentIndex];
33 | if (!c)
34 | return
35 |
36 | this.contentHeight = c.height;
37 | this.contentWidth = c.width;
38 | }
39 |
40 | /// @private
41 | function addChild(child) {
42 | $core.Layout.prototype.addChild.apply(this, arguments)
43 | var children = this.children
44 | var index = children.length - 1
45 | var lastChild = children[index]
46 | lastChild.visibleInView = (index === this.currentIndex)
47 | var update = this._scheduleLayout.bind(this)
48 | child.onChanged('height', update)
49 | child.onChanged('recursiveVisible', update)
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/core/PropertyStorage.qml:
--------------------------------------------------------------------------------
1 | /// object to hold named property synced with underlying storage
2 | LocalStorage {
3 | signal ready; ///< the value now in a sync state with underlying storage
4 | property string name; ///< stored property key name
5 | property string value; ///< stored property value
6 | property string defaultValue; ///< default init value
7 |
8 | ///@private
9 | _checkNameValid: {
10 | if (!this.name)
11 | throw new Error('empty property name')
12 | }
13 |
14 | ///@private
15 | _read: {
16 | this._checkNameValid()
17 | this.getOrDefault(this.name, function(value) {
18 | this._setProperty('value', value)
19 | this.ready()
20 | }.bind(this), this.defaultValue)
21 | }
22 |
23 | ///@private
24 | _write: {
25 | this._checkNameValid()
26 | if (this.value)
27 | this.set(this.name, this.value)
28 | else
29 | this.erase(this.name)
30 | }
31 |
32 | ///@private
33 | onValueChanged: { this._write() }
34 |
35 | ///@private
36 | onNameChanged: {
37 | this._setProperty('value', '')
38 | this._read()
39 | }
40 |
41 | onCompleted: {
42 | if (this.value) {
43 | this._setProperty('value', this.value)
44 | this.ready()
45 | }
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/core/RAIIEventEmitter.qml:
--------------------------------------------------------------------------------
1 | ///@private
2 | EventEmitter {
3 | ///@private
4 | constructor: {
5 | this._onListener = {}
6 | }
7 |
8 | ///@private
9 | function on (name, callback) {
10 | if (!(name in this._eventHandlers)) {
11 | if (name in this._onListener) {
12 | //log('first listener to', name)
13 | this._onListener[name][0](name)
14 | } else if ('' in this._onListener) {
15 | //log('first listener to', name)
16 | this._onListener[''][0](name)
17 | }
18 | if (this._eventHandlers[name])
19 | throw new Error('listener callback added event handler')
20 | }
21 | $core.EventEmitter.prototype.on.call(this, name, callback)
22 | }
23 |
24 | ///@private
25 | function onListener (name, first, last) {
26 | this._onListener[name] = [first, last]
27 | }
28 |
29 | ///@private
30 | function removeAllListeners(name) {
31 | $core.EventEmitter.prototype.removeAllListeners.call(this, name)
32 | if (name in this._onListener)
33 | this._onListener[name][1](name)
34 | else if ('' in this._onListener) {
35 | //log('first listener to', name)
36 | this._onListener[''][1](name)
37 | }
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/core/Radius.qml:
--------------------------------------------------------------------------------
1 | Object {
2 | property real radius; ///< radius for all corners
3 | property real topLeft; ///< top left corner radius
4 | property real topRight; ///< top right corner radius
5 | property real bottomLeft; ///< bottom left corner radius
6 | property real bottomRight; ///< bottom right corner radius
7 |
8 | prototypeConstructor: {
9 | RadiusPrototype.defaultProperty = 'radius';
10 | }
11 |
12 | _updateValue: {
13 | var radius = this.radius
14 | var tl = this.topLeft || radius
15 | var tr = this.topRight || radius
16 | var bl = this.bottomLeft || radius
17 | var br = this.bottomRight || radius
18 | if (tl == tr && bl == br && tl == bl)
19 | this.parent.style('border-radius', tl)
20 | else
21 | this.parent.style('border-radius', tl + 'px ' + tr + 'px ' + br + 'px ' + bl + 'px')
22 | }
23 |
24 | onRadiusChanged,
25 | onTopLeftChanged,
26 | onTopRightChanged,
27 | onBottomLeftChanged,
28 | onBottomRightChanged: {
29 | this._updateValue()
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/core/Rectangle.qml:
--------------------------------------------------------------------------------
1 | /// Colored rectangle with optional rounded corners, border and/or gradient.
2 | Item {
3 | property color color: "#0000"; ///< rectangle background color
4 | property lazy border: Border {} ///< object holding properties of the border
5 | property Gradient gradient; ///< if gradient object was set, it displays gradient instead of solid color
6 | constructor : {
7 | this._context.backend.initRectangle(this)
8 | }
9 |
10 | onColorChanged: {
11 | this.style('background-color', $core.Color.normalize(value))
12 | }
13 |
14 | prototypeConstructor: {
15 | var styleMap = RectanglePrototype._propertyToStyle = Object.create(RectangleBasePrototype._propertyToStyle)
16 | styleMap['color'] = 'background-color'
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/core/Repeater.qml:
--------------------------------------------------------------------------------
1 | ///The simplest view implementation, creates elements without positioning
2 | BaseView {
3 |
4 | ///@private
5 | function positionViewAtIndex() { }
6 |
7 | ///@private
8 | function _layout() {
9 | if (!this.recursiveVisible && !this.offlineLayout) {
10 | this.layoutFinished()
11 | return
12 | }
13 |
14 | var model = this._modelAttached;
15 | if (!model) {
16 | this.layoutFinished()
17 | return
18 | }
19 |
20 | var created = false;
21 | var n = this.count = model.count
22 | var items = this._items
23 | for(var i = 0; i < n; ++i) {
24 | var item = items[i]
25 | if (!item) {
26 | item = this._createDelegate(i)
27 | created = true
28 | }
29 | }
30 | this.layoutFinished()
31 | if (created)
32 | this._context.scheduleComplete()
33 | }
34 |
35 | /// @private creates delegate in given item slot
36 | function _createDelegate(idx) {
37 | var delegate = $core.BaseView.prototype._createDelegate.call(this, idx, function(delegate) {
38 | var parent = this.parent
39 | parent.element.append(delegate.element)
40 | parent.addChild(delegate)
41 | }.bind(this))
42 | return delegate
43 | }
44 |
45 | function _discardItem(item) {
46 | this.parent.removeChild(item)
47 | $core.BaseView.prototype._discardItem.apply(this, arguments)
48 | }
49 |
50 | onLayoutFinished: {
51 | //request layout from parent if it's layout
52 | var parent = this.parent
53 | if (parent && parent._scheduleLayout) {
54 | parent._scheduleLayout()
55 | }
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/core/Request.qml:
--------------------------------------------------------------------------------
1 | ///object for handling XML/HTTP requests
2 | Object {
3 | property bool loading: false; ///< loading flag, is true when request was send and false when answer was recieved or error occured
4 |
5 | /**@param request:Object request object
6 | send request using 'XMLHttpRequest' object*/
7 | function ajax(request) {
8 | if (request.done)
9 | request.done = this._context.wrapNativeCallback(request.done)
10 | if (request.error)
11 | request.error = this._context.wrapNativeCallback(request.error)
12 | this._context.backend.ajax(this, request)
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/core/Row.qml:
--------------------------------------------------------------------------------
1 | /// layout for horizontal oriented content
2 | Layout {
3 | ///@private
4 | onKeyPressed: {
5 | if (!this.handleNavigationKeys)
6 | return false;
7 |
8 | switch (key) {
9 | case 'Left': return this.focusPrevChild()
10 | case 'Right': return this.focusNextChild()
11 | }
12 | }
13 |
14 | ///@private
15 | function _layout() {
16 | if (!this.recursiveVisible && !this.offlineLayout)
17 | return
18 |
19 | var children = this.children;
20 | var p = 0
21 | var h = 0
22 | this.count = children.length
23 |
24 | for(var i = 0; i < children.length; ++i) {
25 | var c = children[i]
26 | if (!('width' in c))
27 | continue
28 |
29 | var rm = c.anchors.rightMargin || c.anchors.margins
30 | var lm = c.anchors.leftMargin || c.anchors.margins
31 |
32 | var b = c.y + c.height
33 | if (b > h)
34 | h = b
35 | c.viewX = p + rm
36 | if (c.visible && c.width > 0)
37 | p += c.width + this.spacing + rm + lm
38 | }
39 | if (p > 0)
40 | p -= this.spacing
41 | this.contentWidth = p
42 | this.contentHeight = h
43 | }
44 |
45 | ///@private
46 | function addChild(child) {
47 | $core.Item.prototype.addChild.apply(this, arguments)
48 |
49 | if (!('width' in child))
50 | return
51 |
52 | var update = this._scheduleLayout.bind(this)
53 | child.onChanged('recursiveVisible', update)
54 | child.onChanged('width', update)
55 | child.on('anchorsMarginsUpdated', update)
56 | this._scheduleLayout()
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/core/ScrollView.qml:
--------------------------------------------------------------------------------
1 | Item {
2 | property enum horizontalScrollBarPolicy { ScrollBarAsNeeded, ScrollBarAlwaysOff, ScrollBarAlwaysOn}: ScrollBarAlwaysOff;
3 | property enum verticalScrollBarPolicy { ScrollBarAsNeeded, ScrollBarAlwaysOff, ScrollBarAlwaysOn};
4 | cssPointerTouchEvents: true;
5 |
6 | constructor: {
7 | this.style({ 'overflow-x': 'hidden', 'overflow-y': 'auto' })
8 | }
9 |
10 | ///@private
11 | function _setStyle(style, value) {
12 | switch(value) {
13 | case ScrollViewPrototype.ScrollBarAsNeeded:
14 | this.style(style, 'auto')
15 | break
16 | case ScrollViewPrototype.ScrollBarAlwaysOn:
17 | this.style(style, 'visible')
18 | break
19 | case ScrollViewPrototype.ScrollBarAlwaysOff:
20 | this.style(style, 'hidden')
21 | break
22 | }
23 | }
24 |
25 | onHorizontalScrollBarPolicyChanged: {
26 | this._setStyle('overflow-x', value)
27 | }
28 |
29 | onVerticalScrollBarPolicyChanged: {
30 | this._setStyle('overflow-y', value)
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/core/SequentialAnimation.qml:
--------------------------------------------------------------------------------
1 | Object {
2 | property bool running: false;
3 | property bool trace: false;
4 |
5 | constructor: {
6 | this._sequence = []
7 | this._currentTarget = null
8 | this._currentProperty = null
9 | }
10 |
11 | onRunningChanged: {
12 | if (value)
13 | {
14 | if ($manifest$disableAnimations || this._sequence.length === 0) {
15 | this.running = false
16 | return
17 | }
18 | this._start(0)
19 | }
20 | }
21 |
22 | function _start(idx) {
23 | for(var i = idx, n = this._sequence.length; i < n; ++i) {
24 | var animation = this._sequence[i]
25 | var target = animation.target
26 | var property = animation.property
27 | var to = animation.to
28 | if (!target || !property || to === undefined) {
29 | log('invalid animation ', this.getComponentPath(), 'without target/property or to')
30 | continue
31 | }
32 | var from = animation.from
33 | if (from !== undefined) {
34 | if (this.trace)
35 | log('animation #' + idx + 'setting initial property value to', from)
36 | target[property] = from
37 | }
38 |
39 | if (target[property] === to) {
40 | if (this.trace)
41 | log('skipping animation #' + idx + ', same value')
42 | continue
43 | }
44 | if (this.trace)
45 | log('starting animation #' + idx, 'target', target.getComponentPath(), 'property', property, 'to', to)
46 | this._currentTarget = target
47 | this._currentProperty = property
48 | target.setAnimation(property, animation)
49 | target[property] = to
50 | return;
51 | }
52 | if (this.trace)
53 | log('animation sequence ', this.getComponentPath(), 'finished')
54 | this.running = false //no valid animation found
55 | return
56 | }
57 |
58 | function _onAnimationRunningChanged(animation, running) {
59 | if (this.trace)
60 | log('animation', animation.getComponentPath(), 'changed running to', running)
61 | if (!running) {
62 | this._currentTarget.resetAnimation(this._currentProperty)
63 | this._currentTarget = this._currentProperty = null
64 | var idx = this._sequence.indexOf(animation)
65 | this._start(idx + 1)
66 | }
67 | }
68 |
69 | function addChild (animation) {
70 | if (animation instanceof $core.Animation)
71 | {
72 | animation.cssTransition = false //we will add keyframe mode here later
73 | this._sequence.push(animation)
74 | this.connectOnChanged(animation, 'running', function(value) {
75 | this._onAnimationRunningChanged(animation, value)
76 | }.bind(this))
77 | }
78 | else
79 | $core.Object.prototype.addChild.call(this, animation)
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/core/Shadow.qml:
--------------------------------------------------------------------------------
1 | /// provides shadow properties adjustment
2 | Object {
3 | property real x; ///< x coordinate
4 | property real y; ///< y coordinate
5 | property color color: "black"; ///< color of the shadow
6 | property real blur; ///< applies a blur effect on the shadow
7 | property real spread; ///< adjusts a spread distance of the shadow
8 |
9 | onXChanged, onYChanged,
10 | onColorChanged, onBlurChanged, onSpreadChanged: {
11 | this.parent._updateStyle(true)
12 | }
13 |
14 | /// @private
15 | function _empty() {
16 | return !this.x && !this.y && !this.blur && !this.spread;
17 | }
18 |
19 | /// @private
20 | function _getFilterStyle() {
21 | var style = this.x + "px " + this.y + "px " + this.blur + "px "
22 | if (this.spread > 0)
23 | style += this.spread + "px "
24 | style += $core.Color.normalize(this.color)
25 | return style
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/core/System.qml:
--------------------------------------------------------------------------------
1 | ///object for storing general info about device and platform
2 | Object {
3 | property string userAgent; ///< browser userAgent value
4 | property string language; ///< platform language
5 | property string browser; ///< browser name
6 | property string vendor; ///< current vendor name
7 | property string os; ///< operation system name
8 | property bool webkit; ///< webkit supported flag
9 | property bool support3dTransforms; ///< CSS transforms3d supported flag
10 | property bool supportTransforms; ///< CSS transforms supported flag
11 | property bool supportTransitions; ///< CSS transitions supported flag
12 | property bool portrait: parent.width < parent.height; ///< portrait oriented screen flag
13 | property bool landscape: !portrait; ///< landscape oriented screen flag
14 | property bool pageActive: true; ///< page active flag
15 | property int screenWidth; ///< device screen width value
16 | property int screenHeight; ///< device screen height value
17 | property int contextWidth: context.width; ///< @private
18 | property int contextHeight: context.height; ///< @private
19 | property int resolutionWidth; ///< app screen width from manifest
20 | property int resolutionHeight; ///< app screen height from manifest
21 | property enum device { Desktop, Tv, Mobile }; ///< device type enumeration, values: Desktop, Tv or Mobile
22 | property enum layoutType { MobileS, MobileM, MobileL, Tablet, Laptop, LaptopL, Laptop4K }; ///< layout type enumeration, values: MobileS, MobileM, MobileL, Tablet, Laptop, LaptopL and Laptop4K
23 | property bool virtualKeyboard: device === System.Tv || device === System.Mobile; ///< used to tweak components which use on-screen keyboard
24 | property bool tapHighlighted: os === "android" || os === "androidttk" || os === "hisense" || os === "ekt" || os === "sagem";
25 | property bool selectByMouse: !(tapHighlighted || os === "webos");
26 |
27 | /// @private
28 | onContextWidthChanged: { this._updateLayoutType() }
29 | /// @private
30 | onContextHeightChanged: { this._updateLayoutType() }
31 |
32 | /// @private
33 | _updateLayoutType: {
34 | if (!this.contextWidth || !this.contextHeight)
35 | return
36 | var min = this.contextWidth;// < this.contextHeight ? this.contextWidth : this.contextHeight
37 |
38 | if (min <= 320)
39 | this.layoutType = this.MobileS
40 | else if (min <= 375)
41 | this.layoutType = this.MobileM
42 | else if (min <= 425)
43 | this.layoutType = this.MobileL
44 | else if (min <= 768)
45 | this.layoutType = this.Tablet
46 | else if (this.contextWidth <= 1024)
47 | this.layoutType = this.Laptop
48 | else if (this.contextWidth <= 1440)
49 | this.layoutType = this.LaptopL
50 | else
51 | this.layoutType = this.Laptop4K
52 | }
53 |
54 | /// @private
55 | constructor: {
56 | this.vendor = $core.vendor
57 | this.device = $core.device
58 | this.os = $core.os
59 |
60 | this.browser = $core.browser
61 | this.userAgent = $core.userAgent
62 | this.language = $core.language
63 |
64 | var ctx = this._context
65 | ctx.language = this.language.replace('-', '_')
66 | this.webkit = this.userAgent.toLowerCase().indexOf('webkit') >= 0
67 |
68 | this.support3dTransforms = ctx.backend.capabilities.csstransforms3d || false
69 | this.supportTransforms = ctx.backend.capabilities.csstransforms || false
70 | this.supportTransitions = ctx.backend.capabilities.csstransitions || false
71 |
72 | this.resolutionWidth = $manifest$resolutionWidth
73 | this.resolutionHeight = $manifest$resolutionHeight
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/core/Text.qml:
--------------------------------------------------------------------------------
1 | /// item with text
2 | Item {
3 | property string text; ///< text to be displayed
4 | property color color; ///< color of the text
5 | property lazy shadow: Shadow { } ///< text shadow object
6 | property lazy font: Font { } ///< text font object
7 | property enum horizontalAlignment { AlignLeft, AlignRight, AlignHCenter, AlignJustify }; ///< text horizontal alignment
8 | property enum verticalAlignment { AlignTop, AlignBottom, AlignVCenter }; ///< text vertical alignment
9 | property enum wrapMode { NoWrap, WordWrap, WrapAnywhere, Wrap }; ///< multiline text wrap mode
10 | property enum textFormat { Html, Text }; ///< only html or text for now
11 | property int paintedWidth; ///< real width of the text without any layout applied
12 | property int paintedHeight; ///< real height of this text without any layout applied
13 | width: paintedWidth;
14 | height: paintedHeight;
15 | property bool offlineLayout;
16 |
17 | ///@private
18 | constructor: {
19 | this._context.backend.initText(this)
20 | if (this.text.length > 0)
21 | this._setText(this.text)
22 | }
23 |
24 | function getClass() { return 'core-text' }
25 |
26 | function registerStyle(style, tag) {
27 | style.addRule(tag, {'width': 'auto', 'height': 'auto'})
28 | }
29 |
30 | ///@private
31 | function _scheduleUpdateSize() {
32 | this._context.delayedAction('text:update-size', this, this._updateSizeImpl)
33 | }
34 |
35 | ///@private
36 | function _setText(html) {
37 | this._context.backend.setText(this, html)
38 | }
39 |
40 | ///@private
41 | function onChanged (name, callback) {
42 | if (!this._updateSizeNeeded) {
43 | switch(name) {
44 | case "right":
45 | case "width":
46 | case "bottom":
47 | case "height":
48 | case "verticalCenter":
49 | case "horizontalCenter":
50 | this._enableSizeUpdate()
51 | }
52 | }
53 | $core.Item.prototype.onChanged.apply(this, arguments);
54 | }
55 |
56 | ///@private
57 | function on(name, callback) {
58 | if (!this._updateSizeNeeded) {
59 | if (name === 'newBoundingBox')
60 | this._enableSizeUpdate()
61 | }
62 | $core.Item.prototype.on.apply(this, arguments)
63 | }
64 |
65 | ///@private
66 | function _updateStyle() {
67 | if (this.shadow && !this.shadow._empty())
68 | this.style('text-shadow', this.shadow._getFilterStyle())
69 | else
70 | this.style('text-shadow', '')
71 | $core.Item.prototype._updateStyle.apply(this, arguments)
72 | }
73 |
74 | ///@private
75 | function _enableSizeUpdate() {
76 | this._updateSizeNeeded = true
77 | this._updateSize()
78 | }
79 |
80 | onRecursiveVisibleChanged: {
81 | if (value && !this.offlineLayout)
82 | this._updateSize()
83 | }
84 |
85 | ///@private
86 | function _updateSize() {
87 | if ((this.offlineLayout || this.recursiveVisible) && (this._updateSizeNeeded || this.clip))
88 | this._scheduleUpdateSize()
89 | }
90 |
91 | ///@private
92 | function _updateSizeImpl() {
93 | if (this.text.length === 0) {
94 | this.paintedWidth = 0
95 | this.paintedHeight = 0
96 | return
97 | }
98 |
99 | this._context.backend.layoutText(this)
100 | }
101 |
102 | onTextChanged: { this._setText(value); this._updateSize() }
103 | onColorChanged: { this.style('color', $core.Color.normalize(value)) }
104 | onWidthChanged: { this._updateSize() }
105 | onHeightChanged: { this._updateSize() }
106 |
107 | onVerticalAlignmentChanged: {
108 | this._enableSizeUpdate()
109 | if ($manifest$requireVerticalTextAlignmentStyle) {
110 | switch(value) {
111 | case this.AlignTop: this.style('-pure-text-vertical-align', 'top'); break
112 | case this.AlignVCenter: this.style('-pure-text-vertical-align', 'middle'); break
113 | case this.AlignBottom: this.style('-pure-text-vertical-align', 'bottom'); break
114 | }
115 | }
116 | }
117 |
118 | onHorizontalAlignmentChanged: {
119 | switch(value) {
120 | case this.AlignLeft: this.style('text-align', 'left'); break
121 | case this.AlignRight: this.style('text-align', 'right'); break
122 | case this.AlignHCenter: this.style('text-align', 'center'); break
123 | case this.AlignJustify: this.style('text-align', 'justify'); break
124 | }
125 | }
126 |
127 | function _updateWSHandling() {
128 | var text = this.textFormat === this.Text
129 | switch(this.wrapMode) {
130 | case this.NoWrap:
131 | this.style({'white-space': text? 'pre': 'nowrap', 'word-break': '' })
132 | break
133 | case this.Wrap:
134 | case this.WordWrap:
135 | this.style({'white-space': text? 'pre-wrap': 'normal', 'word-break': '' })
136 | break
137 | case this.WrapAnywhere:
138 | this.style({ 'white-space': text? 'pre-wrap': 'normal', 'word-break': 'break-all' })
139 | break
140 | }
141 | this._updateSize();
142 | }
143 |
144 | onTextFormatChanged: {
145 | this._updateWSHandling()
146 | }
147 |
148 | onWrapModeChanged: {
149 | this._updateWSHandling()
150 | }
151 |
152 | onOfflineLayoutChanged: {
153 | if (value)
154 | this._enableSizeUpdate()
155 | }
156 | }
157 |
--------------------------------------------------------------------------------
/core/Timer.qml:
--------------------------------------------------------------------------------
1 | /// class handles periodic tasks
2 | Object {
3 | signal triggered; ///< this signal triggered when timer fires
4 | property int interval: 1000; ///< interval, ms
5 | property bool repeat; ///< makes this timer periodic
6 | property bool running; ///< current timer status, true - running, false - paused
7 | property bool triggeredOnStart; ///< fire timer's signal on start or activation
8 |
9 | constructor: {
10 | this._trigger = this._context.wrapNativeCallback(this.triggered.bind(this))
11 | }
12 |
13 | /// restart timer, activate if stopped
14 | restart: { this.stop(); this.start(); }
15 |
16 | /// stops timer
17 | stop: { this.running = false }
18 |
19 | /// starts timer
20 | start: { this.running = true }
21 |
22 | /// @private
23 | onTriggered: {
24 | if (!this.repeat && (!this.triggeredOnStart || this._triggered))
25 | this.running = false
26 | this._triggered = true
27 | }
28 |
29 | /// @private
30 | onCompleted: {
31 | if (this.running && this.triggeredOnStart)
32 | this.triggered()
33 | }
34 |
35 | onRunningChanged: {
36 | this._restart()
37 | if (value && this.triggeredOnStart) {
38 | this._triggered = false
39 | this.triggered()
40 | }
41 | }
42 |
43 | onIntervalChanged,
44 | onRepeatChanged: { this._restart() }
45 |
46 | /// @private
47 | function discard() {
48 | this._clear()
49 | $core.Object.prototype.discard.apply(this)
50 | }
51 |
52 | /// @private
53 | function _clear() {
54 | if (this._timeout) {
55 | clearTimeout(this._timeout);
56 | this._timeout = undefined;
57 | }
58 | if (this._interval) {
59 | clearInterval(this._interval);
60 | this._interval = undefined;
61 | }
62 | }
63 |
64 | /// @private
65 | function _restart() {
66 | this._clear()
67 |
68 | if (!this.running)
69 | return;
70 |
71 | //log("starting timer", this.interval, this.repeat);
72 | var self = this
73 | var context = self._context
74 | if (this.repeat)
75 | this._interval = setInterval(this._trigger, this.interval);
76 | else
77 | this._timeout = setTimeout(this._trigger, this.interval);
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/core/Transform.qml:
--------------------------------------------------------------------------------
1 | /// class controlling object transformation
2 | Object {
3 | property int perspective; ///< perspective transformation
4 | property int translateX; ///< x-translate
5 | property int translateY; ///< y-translate
6 | property int translateZ; ///< z-translate
7 | property real rotateX; ///< x-axis rotation
8 | property real rotateY; ///< y-axis rotation
9 | property real rotateZ; ///< z-axis rotation
10 | property real rotate; ///< rotate relative transform point
11 | property real scaleX; ///< horizontal scale
12 | property real scaleY; ///< vertical scale
13 | property real skewX; ///< horizontal skew
14 | property real skewY; ///< vertical skew
15 |
16 | ///@private
17 | constructor: { this._transforms = new $core.transform.Transform() }
18 |
19 | onPerspectiveChanged: { this._transforms.add('perspective', value, 'px'); this._updateTransform() }
20 | onTranslateXChanged: { this._transforms.add('translateX', value, 'px'); this._updateTransform() }
21 | onTranslateYChanged: { this._transforms.add('translateY', value, 'px'); this._updateTransform() }
22 | onTranslateZChanged: { this._transforms.add('translateZ', value, 'px'); this._updateTransform() }
23 | onRotateXChanged: { this._transforms.add('rotateX', value, 'deg'); this._updateTransform() }
24 | onRotateYChanged: { this._transforms.add('rotateY', value, 'deg'); this._updateTransform() }
25 | onRotateZChanged: { this._transforms.add('rotateZ', value, 'deg'); this._updateTransform() }
26 | onRotateChanged: { this._transforms.add('rotate', value, 'deg'); this._updateTransform() }
27 | onScaleXChanged: { this._transforms.add('scaleX', value); this._updateTransform() }
28 | onScaleYChanged: { this._transforms.add('scaleY', value); this._updateTransform() }
29 | onSkewXChanged: { this._transforms.add('skewX', value, 'deg'); this._updateTransform() }
30 | onSkewYChanged: { this._transforms.add('skewY', value, 'deg'); this._updateTransform() }
31 |
32 | function _updateTransform() {
33 | this.parent.style('transform', this._transforms)
34 | }
35 |
36 | ///@private instead of animating transform property in Item, animate each property in Transform object
37 | ///unfortunately animations are not const, though it's a good idea to make them so (and save on animation instances)
38 | ///this function is meant to be called from non-html backends to animate transformations.
39 | ///see backend.js in platform/pure.femto for details
40 | function _animateAll(animation) {
41 | var transform = this
42 | var transform_properties = [
43 | 'perspective',
44 | 'translateX', 'translateY', 'translateZ',
45 | 'rotateX', 'rotateY', 'rotateZ', 'rotate',
46 | 'scaleX', 'scaleY',
47 | 'skewX', 'skewY'
48 | ]
49 | transform_properties.forEach(function(transform_property) {
50 | var property_animation = new $core.Animation(transform)
51 | $core.core.createObject(property_animation)
52 | property_animation.delay = animation.delay
53 | property_animation.duration = animation.duration
54 | property_animation.easing = animation.easing
55 |
56 | transform.setAnimation(transform_property, property_animation)
57 | })
58 | }
59 |
60 | ///@private
61 | function getAnimation(name) {
62 | var animation = $core.Object.prototype.getAnimation.call(this, name)
63 | if (!animation) {
64 | animation = $core.Object.prototype.getAnimation.call(this.parent, 'transform')
65 | }
66 | return animation
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/core/WheelMixin.qml:
--------------------------------------------------------------------------------
1 | /// mixin for wheel events
2 | BaseMixin {
3 | ///@private
4 | constructor: {
5 | this._bindWheel(this.enabled)
6 | }
7 |
8 | ///@private
9 | function _bindWheel(value) {
10 | if (value && !this._wheelBinder) {
11 | this._wheelBinder = new $core.EventBinder(this.parent.element)
12 | this._wheelBinder.on('wheel', $core.createSignalForwarder(this.parent, 'wheel').bind(this))
13 | this._wheelBinder.on('mousewheel', $core.createSignalForwarder(this.parent, 'wheel').bind(this))
14 | }
15 | if (this._wheelBinder)
16 | this._wheelBinder.enable(value)
17 | }
18 |
19 | ///@private
20 | onEnabledChanged: { this._bindWheel(value) }
21 | }
22 |
--------------------------------------------------------------------------------
/core/gradient.js:
--------------------------------------------------------------------------------
1 | var GradientStop = function(color, position) {
2 | this.color = $core.Color.normalize(color)
3 | this.position = position
4 | }
5 |
6 | var GradientStopPrototype = GradientStop.prototype
7 | GradientStopPrototype.constructor = GradientStop
8 |
9 | GradientStopPrototype.toString = function() {
10 | return this.color + " " + Math.floor(100 * this.position) + "%"
11 | }
12 |
13 | var Gradient = function(orientation, type) {
14 | this.orientation = orientation
15 | this.stops = []
16 | this.type = type
17 | }
18 |
19 | var GradientPrototype = Gradient.prototype
20 | GradientPrototype.constructor = Gradient
21 |
22 | GradientPrototype.add = function(stop) {
23 | this.stops.push(stop)
24 | }
25 |
26 | GradientPrototype.toString = function() {
27 | if (this.type === $core.Gradient.Linear) {
28 | return 'linear-gradient(' + this.orientation + ',' + this.stops.join() + ')'
29 | }
30 | else if (this.type === $core.Gradient.Conical) {
31 | var gradientString = 'conic-gradient(from ' + this.orientation
32 | for(var i = 0; i < this.stops.length; ++i) {
33 | var stop = this.stops[i]
34 | gradientString += ', ' + stop.color.hex() + ' ' + 2 * Math.PI * stop.position + 'rad'
35 | }
36 | gradientString += ')'
37 | return gradientString;
38 | }
39 |
40 | throw new Error("Gradient Type " + this.type + " is not supported")
41 | return "";
42 | }
43 |
44 | exports.GradientStop = GradientStop
45 | exports.Gradient = Gradient
46 |
--------------------------------------------------------------------------------
/core/model.js:
--------------------------------------------------------------------------------
1 | var ModelUpdateNothing = 0
2 | var ModelUpdateInsert = 1
3 | var ModelUpdateRemove = 2
4 | var ModelUpdateUpdate = 3
5 | var ModelUpdateFinish = 4
6 |
7 | exports.ModelUpdate = function() {
8 | this.rows = []
9 | }
10 | exports.ModelUpdate.prototype.constructor = exports.ModelUpdate
11 |
12 | exports.ModelUpdate.prototype.reset = function(model) {
13 | var n = model.count
14 | var rows = this.rows = new Array(n)
15 | for (var i = 0; i < n; ++i)
16 | rows[i] = [i, true]
17 | }
18 |
19 | exports.ModelUpdate.prototype.insert = function(model, begin, end) {
20 | if (begin >= end)
21 | return
22 |
23 | var d = end - begin
24 | var rows = this.rows
25 | var args = [begin, 0]
26 | for(var i = 0; i < d; ++i)
27 | args.push([begin + i, true])
28 | rows.splice.apply(rows, args)
29 | if (rows.length != model.count)
30 | throw new Error('unbalanced insert ' + rows.length + ' + [' + begin + '-' + end + '], model reported ' + model.count)
31 | }
32 |
33 | exports.ModelUpdate.prototype.remove = function(model, begin, end) {
34 | if (begin >= end)
35 | return
36 |
37 | var d = end - begin
38 | var rows = this.rows
39 | rows.splice(begin, d)
40 | if (rows.length != model.count)
41 | throw new Error('unbalanced remove ' + rows.length + ' + [' + begin + '-' + end + '], model reported ' + model.count)
42 | }
43 |
44 | exports.ModelUpdate.prototype.update = function(model, begin, end) {
45 | if (begin >= end)
46 | return
47 |
48 | var rows = this.rows;
49 | for(var i = begin; i < end; ++i)
50 | rows[i][1] = true
51 | }
52 |
53 | exports.ModelUpdate.prototype.clear = function() {
54 | this.rows = []
55 | }
56 |
57 | exports.ModelUpdate.prototype.apply = function(view, skipCheck) {
58 | var rows = this.rows
59 | var currentRange = ModelUpdateNothing
60 | var currentRangeStartedAt = 0
61 | var currentRangeSize = 0
62 | var updated = false
63 |
64 | //log("APPLY ", rows)
65 |
66 | var apply = function(range, index, size) {
67 | if (size === undefined)
68 | size = 1
69 |
70 | if (currentRange === range) {
71 | currentRangeSize += size
72 | return
73 | }
74 |
75 | if (currentRangeSize > 0) {
76 | switch(currentRange) {
77 | case ModelUpdateNothing:
78 | break
79 | case ModelUpdateUpdate:
80 | updated = true
81 | view._updateItems(currentRangeStartedAt, currentRangeStartedAt + currentRangeSize)
82 | break
83 | case ModelUpdateInsert:
84 | updated = true
85 | view._insertItems(currentRangeStartedAt, currentRangeStartedAt + currentRangeSize)
86 | break
87 | case ModelUpdateRemove:
88 | updated = true
89 | view._removeItems(currentRangeStartedAt, currentRangeStartedAt + currentRangeSize)
90 | break
91 | }
92 | }
93 |
94 | currentRange = range
95 | currentRangeStartedAt = index
96 | currentRangeSize = size
97 | }
98 |
99 | var src_n = rows.length
100 | var dst_n = view._items.length
101 | var offset = 0
102 | for(var src = 0; src < src_n; ) {
103 | var row = rows[src]
104 | var dst = row[0] + offset
105 | if (src >= dst_n) {
106 | apply(ModelUpdateInsert, src, src_n - dst_n)
107 | break
108 | } else if (dst === src) {
109 | apply(row[1] || offset !== 0? ModelUpdateUpdate: ModelUpdateNothing, src)
110 | if (offset !== 0)
111 | view._updateDelegateIndex(src)
112 | ++src
113 | ++dst
114 | } else if (dst > src) {
115 | //removing gap
116 | var d = dst - src
117 | apply(ModelUpdateRemove, src, d)
118 | offset += -d
119 | } else { //dst < src
120 | var d = src - dst
121 | if (currentRange === ModelUpdateUpdate && d == currentRangeSize) {
122 | //check here if we have an equivalent range of update,
123 | //signal insert first instead of update (on the next loop iteration)
124 | offset += d
125 | currentRange = ModelUpdateInsert
126 | } else {
127 | offset += d
128 | src += d
129 | apply(ModelUpdateInsert, dst + d, d)
130 | }
131 | }
132 | }
133 | apply(ModelUpdateFinish, dst_n)
134 |
135 | dst_n = view._items.length //update length
136 | if (dst_n > src_n) {
137 | view._removeItems(src_n, dst_n)
138 | } else if (src_n > dst_n) {
139 | view._insertItems(dst_n, src_n)
140 | }
141 | if (!skipCheck && view._items.length != src_n )
142 | throw new Error('unbalanced items update, view: ' + view._items.length + ', update:' + src_n)
143 |
144 | for(var i = 0; i < src_n; ++i) {
145 | var row = rows[i]
146 | row[0] = i
147 | row[1] = false
148 | }
149 | return updated
150 | }
151 |
152 | var ArrayModelWrapper = exports.ArrayModelWrapper = function (data) { this.data = data; this.count = data.length }
153 | ArrayModelWrapper.prototype.get = function(idx) { return { value: this.data[idx] } }
154 | ArrayModelWrapper.prototype.on = function() { }
155 | ArrayModelWrapper.prototype.removeListener = function() { }
156 |
--------------------------------------------------------------------------------
/core/transform.js:
--------------------------------------------------------------------------------
1 | var Value = function(value, unit) {
2 | this.value = value
3 | this.unit = unit
4 | }
5 |
6 | var ValuePrototype = Value.prototype
7 | ValuePrototype.constructor = Value
8 |
9 | ValuePrototype.toString = function() {
10 | var unit = this.unit
11 | return unit != undefined? this.value + unit: this.value
12 | }
13 |
14 | var Transform = function() {
15 | this.transforms = {}
16 | }
17 |
18 | var TransformPrototype = Transform.prototype
19 | TransformPrototype.constructor = Transform
20 |
21 | TransformPrototype.add = function(name, value, unit) {
22 | this.transforms[name] = new Value(value, unit)
23 | }
24 |
25 | TransformPrototype.toString = function() {
26 | var transforms = this.transforms
27 | var str = ''
28 | for(var name in transforms) {
29 | var value = transforms[name]
30 | str += name + '(' + value + ') '
31 | }
32 | return str
33 | }
34 |
35 | exports.Transform = Transform
36 |
37 |
--------------------------------------------------------------------------------
/generate-gamepad-mappings:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python2.7
2 |
3 | import argparse
4 | import json
5 |
6 |
7 | def get_max_size(val):
8 | return {
9 | 'head': 4,
10 | 'axes': 13,
11 | 'button': 45
12 | }.get(val, 0)
13 |
14 |
15 | def get_key_name(val):
16 | return {
17 | 'b': 'button',
18 | 'a': 'axes',
19 | 'h': 'head'
20 | }.get(val, val)
21 |
22 |
23 | def get_button_name(name):
24 | return {
25 | "dpup": "up",
26 | "dpright": "right",
27 | "dpleft": "left",
28 | "dpdown": "down",
29 | "lefty": "leftStickY",
30 | "righty": "rightStickY",
31 | "leftx": "leftStickX",
32 | "rightx": "rightStickX",
33 | "righttrigger": "rightTrigger",
34 | "leftshoulder": "leftBumper",
35 | "rightshoulder": "rightBumper",
36 | "rightstick": "rightStick",
37 | "lefttrigger": "leftTrigger",
38 | "leftstick": "leftStick"
39 | }.get(name, name)
40 |
41 |
42 | def save(filename, data):
43 | with open(filename, 'w') as file_:
44 | file_.write(data)
45 |
46 |
47 | def change_endian(hexString):
48 | return ''.join(sum([(c,d,a,b) for a,b,c,d in zip(*[iter(hexString)]*4)], ()))
49 |
50 |
51 | def parse_file(filePath, resPath):
52 | with open(filePath) as f:
53 | content = f.readlines()
54 | result = {}
55 |
56 | platform = ""
57 | for line in content:
58 | line = line.strip(" \t\r\n")
59 |
60 | if len(line) == 0:
61 | continue
62 |
63 | if line[0] == '#': # skip OS lines
64 | platform = line[2:]
65 | continue
66 |
67 | tokens = line.split(',')
68 | if len(tokens) <= 1:
69 | continue
70 |
71 | gamepad = {}
72 | vendor = tokens[0][:16]
73 | product = tokens[0][16:32]
74 | if platform.lower() == "linux":
75 | vendor = str(int(change_endian(vendor)[8:12], 16))
76 | product = str(int(change_endian(product)[8:12], 16))
77 | else:
78 | vendor = str(int(change_endian(vendor)[0:4], 16))
79 | product = str(int(change_endian(product)[0:4], 16))
80 | id = vendor + ":" + product
81 |
82 | gamepad['name'] = tokens[1]
83 | gamepad['mapping'] = {}
84 | gamepad['mapping']['button'] = [0] * 45 # 45 - max button number
85 | gamepad['mapping']['axes'] = [0] * 13 # 13 - max axes count
86 | max_button = 0
87 | max_axes = 0
88 | for t in tokens:
89 | if len(t) == 0:
90 | continue
91 |
92 | item = t.split(':')
93 | if len(item) <= 1 or item[0] == "platform" or len(item[0]) == 0 or len(item[1]) == 0:
94 | continue
95 |
96 | key = get_key_name(item[1][0])
97 | if key == "head":
98 | continue
99 |
100 | val = get_button_name(item[0])
101 | idx = int(item[1][1:])
102 | gamepad['mapping'][key][idx] = val
103 | if key == "button":
104 | if max_button < idx:
105 | max_button = idx
106 | elif key == "axes":
107 | if max_axes < idx:
108 | max_axes = idx
109 |
110 | gamepad['mapping']['button'] = gamepad['mapping']['button'][:max_button + 1] if max_button > 0 else []
111 | gamepad['mapping']['axes'] = gamepad['mapping']['axes'][:max_axes + 1] if max_axes > 0 else []
112 |
113 | result[id] = gamepad
114 | save(resPath, json.dumps(result, sort_keys=True))
115 |
116 |
117 | if __name__ == '__main__':
118 | parser = argparse.ArgumentParser()
119 |
120 | # input file example
121 | # https://raw.githubusercontent.com/gabomdq/SDL_GameControllerDB/master/gamecontrollerdb.txt
122 | parser.add_argument('file')
123 | parser.add_argument('output')
124 | args = parser.parse_args()
125 | parse_file(args.file, args.output)
126 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "pureqml-core",
3 | "version": "1.2.0",
4 | "description": "QML to HTML5 translator, both for mobile and desktop targets.",
5 | "keywords": [
6 | "qml",
7 | "transpiler",
8 | "translator",
9 | "compiler",
10 | "application",
11 | "mobile",
12 | "templater",
13 | "ui",
14 | "ux",
15 | "cross-platform"
16 | ],
17 | "repository": {
18 | "type": "git",
19 | "url": "git://github.com/pureqml/qmlcore"
20 | },
21 | "bin": {
22 | "pureqml-build": "build"
23 | },
24 | "bugs": {
25 | "url": "https://github.com/pureqml/qmlcore/issues"
26 | },
27 | "scripts": {
28 | "test": "mocha"
29 | },
30 | "author": "PureQML team",
31 | "license": "MIT",
32 | "dependencies": {
33 | "mocha": "^11.0.1",
34 | "sinon": "^12.0.1"
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/partners.json:
--------------------------------------------------------------------------------
1 | {
2 | "pureqml-team": {
3 | "name": "PureQML Team",
4 | "engine": "Octobears Private",
5 | "status": "paper"
6 | },
7 |
8 | "free": {
9 | "name": "General Public",
10 | "engine": "No-Partnership"
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/platform/android/.core.js:
--------------------------------------------------------------------------------
1 | log = console.log.bind(console)
2 |
3 | if (navigator.userAgent.indexOf('Android') >= 0) {
4 | _globals.core.__deviceBackend = function() { return _globals.android.device }
5 |
6 | log("Android detected")
7 | exports.core.vendor = "google"
8 | exports.core.device = 2
9 | exports.core.os = "android"
10 |
11 | exports.core.keyCodes = {
12 | 4: 'Back',
13 | 13: 'Select',
14 | 32: 'Space',
15 | 33: 'PageUp',
16 | 34: 'PageDown',
17 | 37: 'Left',
18 | 38: 'Up',
19 | 39: 'Right',
20 | 40: 'Down',
21 | 48: '0',
22 | 49: '1',
23 | 50: '2',
24 | 51: '3',
25 | 52: '4',
26 | 53: '5',
27 | 54: '6',
28 | 55: '7',
29 | 56: '8',
30 | 57: '9',
31 | 179: 'Pause',
32 | 112: 'Red',
33 | 113: 'Green',
34 | 114: 'Yellow',
35 | 115: 'Blue'
36 | }
37 |
38 | if (window.cordova) {
39 | document.addEventListener("backbutton", function(e) {
40 | var event = new KeyboardEvent("keydown", { bubbles : true });
41 | Object.defineProperty(event, 'keyCode', { get : function() { return 4; } })
42 | document.dispatchEvent(event);
43 | }, false);
44 | } else {
45 | log("'cordova' not defined. 'Back' button will be unhandable. It looks like you forget to include 'cordova.js'")
46 | }
47 |
48 | document.addEventListener("deviceready", function() {
49 | _globals._context.system.vendor = device.manufacturer
50 | }, false);
51 |
52 | log("Android initialized")
53 |
54 | exports.closeApp = function() {
55 | navigator.app.exitApp();
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/platform/android/.manifest:
--------------------------------------------------------------------------------
1 | {
2 | "requires": ["html5", "web", "video.html5"],
3 | "templates": ["*.html", "config.xml"],
4 | "strict": false
5 | }
6 |
--------------------------------------------------------------------------------
/platform/android/build.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | from __future__ import print_function
4 |
5 | import argparse
6 | import os
7 |
8 |
9 | def build(app, title, release):
10 | os.system('rm -rf %s' %app)
11 | res = os.system('cordova create %s com.%s.app %s' %(app, app, title))
12 | if res != 0:
13 | print("Failed to create android app")
14 | return
15 | os.system('rsync -a ./ %s/www --exclude=%s ' %(app,app))
16 | os.system('cp androidIcon.png %s' %(app))
17 | os.system('cp config.xml %s' %(app))
18 | os.chdir(app)
19 |
20 | os.system('cordova platform add android')
21 | os.system('cordova plugin add cordova-plugin-streaming-media')
22 | os.system('cordova plugin add cordova-plugin-device')
23 | os.system('cordova plugin add cordova-plugin-screen-orientation')
24 |
25 | if release:
26 | build = 'cordova build android --release -- '
27 | # TODO: pass release parameters
28 | # os.system(build + '--keystore={{androidBuild.keystore}} --storePassword={{androidBuild.storePassword}} --alias={{androidBuild.alias}} --password={{androidBuild.password}}')
29 | else:
30 | os.system('cordova build android')
31 |
32 | os.chdir('..')
33 |
34 |
35 | parser = argparse.ArgumentParser('pureqml cordova android build tool')
36 | parser.add_argument('--app', '-a', help='application name', default="app")
37 | parser.add_argument('--title', '-t', help='application title', default="App")
38 | parser.add_argument('--release', '-r', help='build release apk', default=False)
39 | args = parser.parse_args()
40 |
41 |
42 | res = os.system('cordova --version')
43 | if res == 0:
44 | build(args.app, args.title, args.release)
45 | else:
46 | print('Install "cordova" first: https://cordova.apache.org/docs/en/latest/guide/cli/')
47 |
--------------------------------------------------------------------------------
/platform/android/device.js:
--------------------------------------------------------------------------------
1 | var Device = function(ui) {
2 | function onDeviceReady() {
3 | ui.deviceId = device.uuid
4 | ui.modelName = device.model
5 | ui.firmware = device.version
6 | ui.runtime = 'cordova'
7 | }
8 | ui._context.document.on('deviceready', onDeviceReady);
9 |
10 | var screen = window.screen
11 | if (screen) {
12 | ui.lockOrientation = function(orientation) { screen.orientation.lock(orientation) }.bind(ui)
13 | ui.unlockOrientation = function() { screen.orientation.unlock() }.bind(ui)
14 | } else {
15 | log("'screen' is undefined, add 'cordova-plugin-screen-orientation' plugin")
16 | }
17 | }
18 |
19 | exports.createDevice = function(ui) {
20 | return new Device(ui)
21 | }
22 |
23 | exports.Device = Device
24 |
--------------------------------------------------------------------------------
/platform/android/dist/androidIcon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pureqml/qmlcore/4b1eff3a3478ba8ddc51e363e99c6808212094db/platform/android/dist/androidIcon.png
--------------------------------------------------------------------------------
/platform/android/dist/config.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | {{ title }}
4 |
5 | {{ description | default('Override app description in manifest') }}
6 |
7 |
8 | {{ author.name | default('John Doe') if author is defined else 'John Doe' }}
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/platform/android/dist/index.html:
--------------------------------------------------------------------------------
1 | {% extends "index.html" %}
2 | {% block head %}
3 |
4 |
5 |
6 | {{ super() }}
7 | {% endblock %}
8 |
--------------------------------------------------------------------------------
/platform/commercial/.core.js:
--------------------------------------------------------------------------------
1 | /*** @using { commercial.PureQmlSplash } **/
2 |
--------------------------------------------------------------------------------
/platform/commercial/.manifest:
--------------------------------------------------------------------------------
1 | { "standalone": false }
2 |
--------------------------------------------------------------------------------
/platform/commercial/PureQmlSplash.qml:
--------------------------------------------------------------------------------
1 | Rectangle {
2 | property bool showGlow;
3 | anchors.fill: context;
4 | color: "#212121";
5 |
6 | Text {
7 | anchors.bottom: logo.top;
8 | anchors.horizontalCenter: parent.horizontalCenter;
9 | text: "powered by";
10 | font.weight: 300;
11 | font.pixelSize: 36;
12 | color: "#eee";
13 | }
14 |
15 | Image {
16 | anchors.centerIn: parent;
17 | source: "res/pureqml-splash-shadow.png";
18 | opacity: parent.showGlow ? 1.0 : 0.0;
19 |
20 | Behavior on opacity { Animation { duration: 3000; } }
21 | }
22 |
23 | Image {
24 | id: logo;
25 | anchors.centerIn: parent;
26 | source: "res/pureqml-splash-logo.png";
27 | }
28 |
29 | Timer {
30 | interval: 3000;
31 | triggeredOnStart: true;
32 | running: true;
33 | repeat: true;
34 |
35 | onTriggered: { this.parent.showGlow = !this.parent.showGlow }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/platform/commercial/dist/res/pureqml-splash-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pureqml/qmlcore/4b1eff3a3478ba8ddc51e363e99c6808212094db/platform/commercial/dist/res/pureqml-splash-logo.png
--------------------------------------------------------------------------------
/platform/commercial/dist/res/pureqml-splash-shadow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pureqml/qmlcore/4b1eff3a3478ba8ddc51e363e99c6808212094db/platform/commercial/dist/res/pureqml-splash-shadow.png
--------------------------------------------------------------------------------
/platform/core/.manifest:
--------------------------------------------------------------------------------
1 | {
2 | "standalone": false,
3 | "properties": {
4 | "virtual": {
5 | "width": 1920,
6 | "height": 1080
7 | },
8 |
9 | "disableAnimations": false,
10 | "requireExplicitRecursiveVisibilityStyle": true,
11 | "requireVerticalTextAlignmentStyle": true,
12 |
13 | "html5.prefix": "",
14 |
15 | "style.font" : {
16 | "family": "Arial",
17 | "pixelSize": 16,
18 | "pointSize": 0,
19 | "lineHeight": 1.2,
20 | "weight": 400
21 | },
22 |
23 | "trace" : {
24 | "keys" : false,
25 | "focus": false,
26 | "listeners": false
27 | },
28 |
29 | "log": { "disable": false },
30 |
31 | "system.fingerprint": false
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/platform/debug.console.re/.core.js:
--------------------------------------------------------------------------------
1 | log = console.re.log.bind(console.re)
2 |
--------------------------------------------------------------------------------
/platform/debug.console.re/.manifest:
--------------------------------------------------------------------------------
1 | { "requires": ["html5"] }
2 |
--------------------------------------------------------------------------------
/platform/debug.console.re/dist/index.html:
--------------------------------------------------------------------------------
1 | {% extends "index.html" %}
2 | {% block head %}
3 |
4 | {{ super() }}
5 | {% endblock %}
6 |
--------------------------------------------------------------------------------
/platform/electronjs/.core.js:
--------------------------------------------------------------------------------
1 | exports.core.device = 0
2 | var userAgent = exports.core.userAgent.toLowerCase()
3 | if (userAgent.indexOf("linux")) {
4 | exports.core.os = "Linux"
5 | } else if (userAgent.indexOf("windows")) {
6 | exports.core.os = "Windows"
7 | } else if (userAgent.indexOf("mac")) {
8 | exports.core.os = "MacOS"
9 | exports.core.vendor = "Apple"
10 | } else {
11 | exports.core.os = "electronjs"
12 | }
13 |
14 | _globals.core.__deviceBackend = function() { return _globals.electronjs.device }
15 |
--------------------------------------------------------------------------------
/platform/electronjs/.manifest:
--------------------------------------------------------------------------------
1 | {
2 | "requires": ["html5", "web"],
3 | "templates": ["*.html", "main.js", "package.json"]
4 | }
5 |
--------------------------------------------------------------------------------
/platform/electronjs/device.js:
--------------------------------------------------------------------------------
1 | var Device = function(ui) {
2 | var userAgent = _globals.core.userAgent.toLowerCase()
3 | var osToken = userAgent.substring(userAgent.indexOf("(") + 1, userAgent.indexOf(")"))
4 | var tokens = osToken.split(";")
5 | if (tokens.length > 0)
6 | ui.modelName = tokens[0]
7 | if (tokens.length > 1)
8 | ui.sdk = tokens[1]
9 | }
10 |
11 | exports.createDevice = function(ui) {
12 | return new Device(ui)
13 | }
14 |
15 | exports.Device = Device
16 |
--------------------------------------------------------------------------------
/platform/electronjs/dist/main.js:
--------------------------------------------------------------------------------
1 | const {app, BrowserWindow} = require('electron')
2 | const path = require('path')
3 |
4 | let win
5 |
6 | function createWindow () {
7 | // Create the browser window.
8 | win = new BrowserWindow({
9 | width: {{ resolutionWidth | default(1280) }},
10 | height: {{ resolutionHeight | default(720) }},
11 | webPreferences: {
12 | preload: path.join(__dirname, 'preload.js')
13 | }
14 | })
15 |
16 | win.loadFile('index.html')
17 |
18 | // TODO: uncomment for debug
19 | // win.webContents.openDevTools()
20 |
21 | win.on('closed', function () { win = null })
22 | }
23 |
24 | app.on('ready', createWindow)
25 |
26 | app.on('window-all-closed', function () {
27 | if (process.platform !== 'darwin') app.quit()
28 | })
29 |
30 | app.on('activate', function () {
31 | if (win === null)
32 | createWindow()
33 | })
34 |
--------------------------------------------------------------------------------
/platform/electronjs/dist/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "{{ title | default('pureqml') }}",
3 | "version": "{{ version | default('1.0.0') }}",
4 | "description": "{{ description | default('') }}",
5 | "main": "main.js",
6 | "scripts": {
7 | "start": "electron ."
8 | },
9 | "repository": "https://github.com/electron/electron-quick-start",
10 | "keywords": [
11 | "Electron",
12 | "quick",
13 | "start",
14 | "tutorial",
15 | "demo"
16 | ],
17 | "author": "{{ author.name | default('Jhon Doe') if author is defined else 'Jhon Doe' }}",
18 | "license": "{{ license | default('MIT') }}",
19 | "devDependencies": {
20 | "electron": "^7.1.3"
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/platform/electronjs/dist/preload.js:
--------------------------------------------------------------------------------
1 | // All of the Node.js APIs are available in the preload process.
2 | // It has the same sandbox as a Chrome extension.
3 | window.addEventListener('DOMContentLoaded', () => {
4 | const replaceText = (selector, text) => {
5 | const element = document.getElementById(selector)
6 | if (element) element.innerText = text
7 | }
8 |
9 | for (const type of ['chrome', 'node', 'electron']) {
10 | replaceText(`${type}-version`, process.versions[type])
11 | }
12 | })
13 |
--------------------------------------------------------------------------------
/platform/electronjs/dist/renderer.js:
--------------------------------------------------------------------------------
1 | // This file is required by the index.html file and will
2 | // be executed in the renderer process for that window.
3 | // No Node.js APIs are available in this process because
4 | // `nodeIntegration` is turned off. Use `preload.js` to
5 | // selectively enable features needed in the rendering
6 | // process.
7 |
--------------------------------------------------------------------------------
/platform/html5.canvas/.core.js:
--------------------------------------------------------------------------------
1 | _globals._backend = function() { return _globals.html5.canvas.backend }
2 |
--------------------------------------------------------------------------------
/platform/html5.canvas/.manifest:
--------------------------------------------------------------------------------
1 | { "requires": ["html5", "pure"], "standalone": false }
2 |
--------------------------------------------------------------------------------
/platform/html5.canvas/backend.js:
--------------------------------------------------------------------------------
1 | exports.capabilities = { }
2 |
3 | var html = null
4 | var runtime = null
5 | var rootItem = null
6 | var canvas = null
7 |
8 | var proxy = [
9 | 'requestAnimationFrame', 'cancelAnimationFrame',
10 | 'enterFullscreenMode', 'exitFullscreenMode', 'inFullscreenMode',
11 | 'loadImage',
12 | //'initText', 'layoutText', //this should not be proxy, implement this backend methods
13 | ]
14 |
15 | exports.initImage = function(image) {
16 | html.initImage(image)
17 | image.element.ui = image
18 | image.element._image = image._image
19 | image.onChanged('status', function() { image.element.update() } )
20 | }
21 |
22 | var Renderer = function(canvas) {
23 | this.canvas = canvas
24 | }
25 |
26 | Renderer.prototype.constructor = Renderer
27 |
28 | Renderer.prototype.setClip = function(rect) {
29 | this.canvas.rect(rect.l, rect.t, rect.width(), rect.height())
30 | this.canvas.clip()
31 | }
32 |
33 | Renderer.prototype.fillRect = function(rect, color) {
34 | if (color.a < 1)
35 | return
36 |
37 | this.canvas.globalAlpha = color.a / 255
38 | this.canvas.fillStyle = color.rgba()
39 | this.canvas.fillRect(rect.l, rect.t, rect.width(), rect.height())
40 | }
41 |
42 | Renderer.prototype.drawImage = function(rect, image, el) {
43 | var ui = el.ui
44 | this.canvas.globalAlpha = ui.opacity
45 | if (ui.sourceHeight > 0 && ui.sourceWidth > 0) {
46 | this.canvas.drawImage(image,
47 | 0, 0, ui.sourceWidth, ui.sourceHeight,
48 | rect.l, rect.t, ui.width, ui.height)
49 | }
50 | }
51 |
52 | exports.init = function(ctx) {
53 | log('init')
54 | html = _globals.html5.html
55 | runtime = _globals.pure.runtime
56 |
57 | proxy.forEach(function(name) {
58 | exports[name] = html[name]
59 | })
60 |
61 | ctx.options.tag = 'canvas'
62 | html.init(ctx)
63 | ctx.canvasElement = ctx.element
64 | ctx.canvas = ctx.element.dom
65 | ctx._updatedItems = []
66 | ctx.element = exports.createElement(ctx, ctx.getTag())
67 |
68 | var resizeCanvas = function() {
69 | log('resizing canvas')
70 | var canvas = ctx.canvas
71 | canvas.setAttribute('width', ctx.width)
72 | canvas.setAttribute('height', ctx.height)
73 | ctx.renderer = new Renderer(canvas.getContext("2d"))
74 | runtime.renderFrame(ctx)
75 | }
76 | resizeCanvas()
77 | ctx.onChanged('width', resizeCanvas)
78 | ctx.onChanged('height', resizeCanvas)
79 |
80 | {
81 | var Element = runtime.Element
82 | Element.prototype.setHtml = function(html) {
83 | //log('setHtml stub')
84 | this.layoutText()
85 | }
86 |
87 | Element.prototype.layoutText = function() {
88 | //log('layout text stub')
89 | }
90 | }
91 | }
92 |
93 | exports.run = function(ctx) {
94 | log('calling redraw')
95 | ctx.canvasElement.style('visibility', 'visible')
96 | runtime.renderFrame(ctx)
97 | }
98 |
99 | exports.createElement = function(ctx, tag) {
100 | if (runtime === null)
101 | runtime = _globals.pure.runtime //fixme: this is called from StyleSheet too early (ctor?), fix initialisation order!
102 | if (html === null)
103 | html = _globals.html5.html
104 |
105 | if (tag === 'style')
106 | return new html.Element(ctx, tag)
107 | else
108 | return new runtime.Element(ctx, tag)
109 | }
110 |
111 | exports.initText = function(text) {
112 | var element = text.element
113 | element._offsetX = 0
114 | element._offsetY = 0
115 | element.ui = text
116 | element.update()
117 | }
118 |
119 | exports.layoutText = function(text) {
120 | //log('layoutText stub')
121 | }
122 |
--------------------------------------------------------------------------------
/platform/html5.fingerprint/.manifest:
--------------------------------------------------------------------------------
1 | {
2 | "standalone": false,
3 | "requires": ["html5"],
4 | "properties": { "system.fingerprint": true }
5 | }
6 |
--------------------------------------------------------------------------------
/platform/html5.fingerprint/fingerprint.js:
--------------------------------------------------------------------------------
1 | var Fingerprint = function() {
2 | log("creating fingerprint")
3 | this.__visited = []
4 | this.__text = ''
5 | }
6 |
7 | var FingerprintPrototype = Fingerprint.prototype
8 |
9 | FingerprintPrototype._update = function(value, name) {
10 | if (!value || this.__visited.indexOf(value) >= 0)
11 | return
12 |
13 | this.__visited.push(value)
14 | var type = typeof value
15 | if (type === 'object') {
16 | if ('length' in value) {
17 | for(var i = 0; i < value.length; ++i) {
18 | this._update(value[i])
19 | }
20 | } else {
21 | for (var k in value) {
22 | this._update(value[k], k)
23 | }
24 | }
25 | return
26 | } else if (type === 'function') {
27 | return
28 | }
29 |
30 | //log("fingerprint update", value, name !== undefined? name: '')
31 | if (value !== undefined && value !== null)
32 | this.__text += String(value)
33 | this.__text += "\0"
34 | }
35 |
36 | FingerprintPrototype.update = function() {
37 | for(var i = 0; i < arguments.length; ++i) {
38 | this._update(arguments[i])
39 | }
40 | }
41 |
42 | FingerprintPrototype.finalize = function() {
43 | var r = $html5.fingerprint.sha1.sha1(this.__text)
44 | this.__text = ''
45 | return r
46 | }
47 |
48 | exports.Fingerprint = Fingerprint
49 |
--------------------------------------------------------------------------------
/platform/html5.fingerprint/sha1.js:
--------------------------------------------------------------------------------
1 | /**
2 | *
3 | * Secure Hash Algorithm (SHA1)
4 | * http://www.webtoolkit.info/
5 | *
6 | **/
7 |
8 | var sha1 = function (msg) {
9 | function rotate_left(n,s) {
10 | var t4 = ( n<>>(32-s));
11 | return t4;
12 | };
13 |
14 | function lsb_hex(val) {
15 | var str="";
16 | var i;
17 | var vh;
18 | var vl;
19 |
20 | for( i=0; i<=6; i+=2 ) {
21 | vh = (val>>>(i*4+4))&0x0f;
22 | vl = (val>>>(i*4))&0x0f;
23 | str += vh.toString(16) + vl.toString(16);
24 | }
25 | return str;
26 | };
27 |
28 | function cvt_hex(val) {
29 | var str="";
30 | var i;
31 | var v;
32 |
33 | for( i=7; i>=0; i-- ) {
34 | v = (val>>>(i*4))&0x0f;
35 | str += v.toString(16);
36 | }
37 | return str;
38 | };
39 |
40 |
41 | function Utf8Encode(string) {
42 | string = string.replace(/\r\n/g,"\n");
43 | var utftext = "";
44 |
45 | for (var n = 0; n < string.length; n++) {
46 |
47 | var c = string.charCodeAt(n);
48 |
49 | if (c < 128) {
50 | utftext += String.fromCharCode(c);
51 | }
52 | else if((c > 127) && (c < 2048)) {
53 | utftext += String.fromCharCode((c >> 6) | 192);
54 | utftext += String.fromCharCode((c & 63) | 128);
55 | }
56 | else {
57 | utftext += String.fromCharCode((c >> 12) | 224);
58 | utftext += String.fromCharCode(((c >> 6) & 63) | 128);
59 | utftext += String.fromCharCode((c & 63) | 128);
60 | }
61 |
62 | }
63 |
64 | return utftext;
65 | };
66 |
67 | var blockstart;
68 | var i, j;
69 | var W = new Array(80);
70 | var H0 = 0x67452301;
71 | var H1 = 0xEFCDAB89;
72 | var H2 = 0x98BADCFE;
73 | var H3 = 0x10325476;
74 | var H4 = 0xC3D2E1F0;
75 | var A, B, C, D, E;
76 | var temp;
77 |
78 | msg = Utf8Encode(msg);
79 |
80 | var msg_len = msg.length;
81 |
82 | var word_array = new Array();
83 | for( i=0; i>>29 );
111 | word_array.push( (msg_len<<3)&0x0ffffffff );
112 |
113 |
114 | for ( blockstart=0; blockstart= 0)
28 | exports.core.browser = "Chromium"
29 | else if (exports.core.userAgent.indexOf('Chrome') >= 0)
30 | exports.core.browser = "Chrome"
31 | else if (exports.core.userAgent.indexOf('Opera') >= 0)
32 | exports.core.browser = "Opera"
33 | else if (exports.core.userAgent.indexOf('Firefox') >= 0)
34 | exports.core.browser = "Firefox"
35 | else if (exports.core.userAgent.indexOf('Safari') >= 0)
36 | exports.core.browser = "Safari"
37 | else if (exports.core.userAgent.indexOf('MSIE') >= 0)
38 | exports.core.browser = "IE"
39 | else if (exports.core.userAgent.indexOf('YaBrowser') >= 0)
40 | exports.core.browser = "Yandex"
41 | else
42 | exports.core.browser = ''
43 |
44 |
45 | _globals._backend = function() { return _globals.html5.html }
46 | _globals.core.__locationBackend = function() { return _globals.html5.location }
47 | _globals.core.__localStorageBackend = function() { return _globals.html5.localstorage }
48 |
--------------------------------------------------------------------------------
/platform/html5/.manifest:
--------------------------------------------------------------------------------
1 | {
2 | "standalone": false,
3 | "requires": ["core"],
4 | "properties": {
5 | "cssAutoClassificator": false,
6 | "cssDisableTransitions": false,
7 | "cssDisableTransformations": false,
8 | "expectRunContextEvent": false,
9 | "requireExplicitRecursiveVisibilityStyle": false,
10 | "requireVerticalTextAlignmentStyle": false
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/platform/html5/Stylesheet.qml:
--------------------------------------------------------------------------------
1 | Object {
2 | constructor: {
3 | var context = this._context
4 |
5 | var style = this.style = context.createElement('style')
6 | style.dom.type = 'text/css'
7 |
8 | var divId = context.options.id
9 |
10 | var div = document.getElementById(context, divId)
11 | var topLevel = div === null
12 |
13 | var tapHighlighted = context.system.tapHighlighted
14 |
15 | //var textAdjust = window.Modernizr.prefixedCSS('text-size-adjust') + ": 100%; "
16 | style.setHtml(
17 | //"html { " + textAdjust + " }" +
18 | "div#" + divId + " { position: absolute; visibility: hidden; left: 0px; top: 0px; }" +
19 | (tapHighlighted ? this.mangleRule('div', "{ -webkit-tap-highlight-color: rgba(255, 255, 255, 0); -webkit-focus-ring-color: rgba(255, 255, 255, 0); outline: none; }") : "") +
20 | (topLevel? "body { padding: 0; margin: 0; border: 0px; overflow: hidden; }": "") + //fixme: do we need style here in non-top-level mode?
21 | this.mangleRule('video', "{ position: absolute; }") + //fixme: do we need position rule if it's item?
22 | this.mangleRule('img', "{ position: absolute; -webkit-touch-callout: none; }")
23 | )
24 | var head = _globals.html5.html.getElement(context, 'head')
25 | head.append(style)
26 | head.updateStyle()
27 |
28 | this._addRule = _globals.html5.html.createAddRule(style.dom).bind(this)
29 | this._lastId = 0
30 | }
31 |
32 | function allocateClass(prefix) {
33 | var globalPrefix = $manifest$html5$prefix
34 | return (globalPrefix? globalPrefix: '') + prefix + '-' + this._lastId++
35 | }
36 |
37 | function mangleSelector(selector) {
38 | var prefix = $manifest$html5$prefix
39 | if (prefix)
40 | return selector + '.' + prefix + 'core-item'
41 | else
42 | return selector
43 | }
44 |
45 | function mangleRule(selector, rule) {
46 | return this.mangleSelector(selector) + ' ' + rule + ' '
47 | }
48 |
49 | function addRule(selector, rule) {
50 | this._addRule(selector, rule)
51 | }
52 |
53 | }
54 |
--------------------------------------------------------------------------------
/platform/html5/cache.js:
--------------------------------------------------------------------------------
1 | var getTime = function() { return Math.floor(new Date().getTime() / 1000) }
2 |
3 | var Entry = function() {
4 | this.created = getTime()
5 | this.waiters = []
6 | this.invoker = null
7 | }
8 |
9 | Entry.prototype.expired = function(ttl) {
10 | return getTime() - this.created >= ttl
11 | }
12 |
13 | Entry.prototype.set = function(result) {
14 | this.created = getTime()
15 | var invoker = this.invoker = _globals.core.safeCall(null, [result], function(ex) { log("cache entry callback failed: ", ex, ex.stack) })
16 | while(this.waiters.length) {
17 | var waiters = this.waiters
18 | this.waiters = []
19 | waiters.forEach(invoker)
20 | }
21 | this.waiters = null
22 | }
23 |
24 | Entry.prototype.wait = function(callback) {
25 | if (this.invoker !== null)
26 | this.invoker(callback)
27 | else
28 | this.waiters.push(callback)
29 | }
30 |
31 | var Cache = function(create, ttl) {
32 | if (!create)
33 | throw new Error("create callback is required")
34 | this._create = create
35 | this._ttl = ttl || 3600
36 | this._cache = {}
37 | setInterval(this.cleanup.bind(this), this._ttl / 2 * 1000)
38 | }
39 |
40 | Cache.prototype.get = function(key, callback) {
41 | var entry = this._cache[key]
42 | if (entry === undefined || entry.expired(this._ttl)) {
43 | this._cache[key] = entry = new Entry()
44 | this._create(key, function(result) {
45 | entry.set(result)
46 | })
47 | }
48 | entry.wait(callback)
49 | }
50 |
51 | Cache.prototype.cleanup = function() {
52 | for(var k in this._cache) {
53 | var entry = this._cache[k]
54 | if (entry.expired(this._ttl)) {
55 | delete this._cache[k]
56 | }
57 | }
58 | }
59 |
60 | exports.Cache = Cache
61 |
--------------------------------------------------------------------------------
/platform/html5/dist/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | {% block title %}{{ title }}{% endblock %}
5 |
6 |
7 | {% block head %}{% endblock %}
8 |
9 |
10 |
11 | {% block content %}{% endblock %}
12 | {% block mainscript %}
13 |
14 | {% endblock %}
15 | {% block footer %}{% endblock %}
16 |
17 |
18 |
--------------------------------------------------------------------------------
/platform/html5/localstorage.js:
--------------------------------------------------------------------------------
1 | var LocalStorage = function(parent) {
2 | if (parent && parent.name !== undefined) {
3 | // TODO: implement properties sunchronization using parent._setProperty() and parent.ready()
4 | }
5 | this._storage = window.localStorage;
6 | if (!this._storage)
7 | throw new Error("no local storage support")
8 | }
9 |
10 | LocalStorage.prototype.get = function(name, callback, error) {
11 | var value = this._storage.getItem(name)
12 | if (value !== null)
13 | callback(value)
14 | else
15 | error(new Error('no item with name ' + name))
16 | }
17 |
18 | LocalStorage.prototype.set = function(name, value) {
19 | this._storage.setItem(name, value)
20 | }
21 |
22 | LocalStorage.prototype.erase = function(name, error) {
23 | this._storage.removeItem(name)
24 | }
25 |
26 | exports.createLocalStorage = function(parent) {
27 | return new LocalStorage(parent)
28 | }
29 |
30 | exports.LocalStorage = LocalStorage
31 |
--------------------------------------------------------------------------------
/platform/html5/location.js:
--------------------------------------------------------------------------------
1 | var Location = function(ui) {
2 | this._ui = ui
3 | var location = window.location
4 | this.updateActualValues()
5 | var self = this
6 | var context = ui._context
7 | context.window.on("hashchange", function() { self._ui.hash = location.hash }.bind(this))
8 | context.window.on("popstate", function() { self.updateActualValues() }.bind(this))
9 | }
10 |
11 | Location.prototype.updateActualValues = function() {
12 | var ui = this._ui
13 | var windowContext = ui._context.window.dom
14 | ui.hash = windowContext.location.hash
15 | ui.href = windowContext.location.href
16 | ui.port = windowContext.location.port
17 | ui.host = windowContext.location.host
18 | ui.origin = windowContext.location.origin
19 | ui.hostname = windowContext.location.hostname
20 | ui.pathname = windowContext.location.pathname
21 | ui.protocol = windowContext.location.protocol
22 | ui.search = windowContext.location.search
23 | ui.state = windowContext.history.state
24 | }
25 |
26 | Location.prototype.changeHref = function(href) {
27 | this._ui._context.window.dom.location.href = href
28 | this.updateActualValues()
29 | }
30 |
31 | Location.prototype.reload = function() {
32 | this._ui._context.window.dom.location.reload()
33 | }
34 |
35 | Location.prototype.pushState = function(state, title, url) {
36 | var ui = this._ui
37 | var windowContext = ui._context.window.dom
38 | if (windowContext.location.hostname) {
39 | windowContext.history.pushState(state, title, url)
40 | this.updateActualValues()
41 | } else {
42 | ui._context.document.title = title
43 | this._ui.state = state
44 | }
45 | }
46 |
47 | exports.createLocation = function(ui) {
48 | return new Location(ui)
49 | }
50 |
51 | exports.Location = Location
52 |
--------------------------------------------------------------------------------
/platform/ios/.core.js:
--------------------------------------------------------------------------------
1 | log = console.log.bind(console)
2 |
3 | _globals.core.__deviceBackend = function() { return _globals.ios.device }
4 | _globals.core.__videoBackends.ios = function() { return _globals.ios.video }
5 |
6 | log("iOS detected")
7 | exports.core.vendor = "apple"
8 | exports.core.device = 2
9 | exports.core.os = "ios"
10 |
11 | exports.closeApp = function() {
12 | navigator.app.exitApp();
13 | }
14 |
--------------------------------------------------------------------------------
/platform/ios/.manifest:
--------------------------------------------------------------------------------
1 | {
2 | "requires": ["video.html5"],
3 | "templates": ["*.html", "config.xml", "build.py"]
4 | }
5 |
--------------------------------------------------------------------------------
/platform/ios/build.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | from __future__ import print_function
4 |
5 | import argparse
6 | import os
7 |
8 |
9 | def build(app, title):
10 | os.system('ls ./')
11 | os.system('rm -rf %s' %app)
12 | res = os.system('cordova create %s com.%s.app %s' %(title, app, title))
13 | if res != 0:
14 | print("Failed to create ios app")
15 | return
16 | os.system('rsync -a ./ %s/www --exclude=%s ' %(app,app))
17 | os.system('cp config.xml %s' %(app))
18 |
19 | os.chdir(app)
20 | os.system('cordova platform add ios@5.1.0')
21 | os.system('cordova plugin add cordova-plugin-device')
22 | os.system('cordova plugin add cordova-plugin-screen-orientation')
23 | os.system('cordova plugin add cordova-plugin-wkwebview-engine')
24 | os.system('cordova build ios')
25 | os.system('cp -r ../icons/* ./platforms/ios/%s/Images.xcassets/AppIcon.appiconset' %(title))
26 | os.system('cordova run --ios')
27 | os.chdir('..')
28 |
29 |
30 | parser = argparse.ArgumentParser('qmlcore build tool')
31 | parser.add_argument('--app', '-a', help='application name', default="app")
32 | parser.add_argument('--title', '-t', help='application title', default="App")
33 | args = parser.parse_args()
34 |
35 |
36 | res = os.system('cordova --version')
37 | if res == 0:
38 | build(args.app, args.title)
39 | else:
40 | print('Install "cordova" first: https://cordova.apache.org/docs/en/latest/guide/cli/')
41 |
--------------------------------------------------------------------------------
/platform/ios/device.js:
--------------------------------------------------------------------------------
1 | var Device = function(ui) {
2 | function onDeviceReady() {
3 | ui.deviceId = device.uuid
4 | ui.modelName = device.model
5 | ui.firmware = device.version
6 | ui.sdk = device.version
7 | if (clientInformation && clientInformation.language)
8 | ui.language = device.version
9 | }
10 | ui._context.document.on('deviceready', onDeviceReady);
11 |
12 | var screen = window.screen
13 | if (screen) {
14 | ui.lockOrientation = function(orientation) { screen.orientation.lock(orientation) }.bind(ui)
15 | ui.unlockOrientation = function() { screen.orientation.unlock() }.bind(ui)
16 | } else {
17 | log("'screen' is undefined, add 'cordova-plugin-screen-orientation' plugin")
18 | }
19 | }
20 |
21 | exports.createDevice = function(ui) {
22 | return new Device(ui)
23 | }
24 |
25 | exports.Device = Device
26 |
--------------------------------------------------------------------------------
/platform/ios/dist/config.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | {{ title }}
4 |
5 | {{ description | default('Override app description in manifest') }}
6 |
7 |
8 | {{ author.name | default('John Doe') if author is defined else 'John Doe' }}
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/platform/ios/dist/icons/icon-1024.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pureqml/qmlcore/4b1eff3a3478ba8ddc51e363e99c6808212094db/platform/ios/dist/icons/icon-1024.png
--------------------------------------------------------------------------------
/platform/ios/dist/icons/icon-20.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pureqml/qmlcore/4b1eff3a3478ba8ddc51e363e99c6808212094db/platform/ios/dist/icons/icon-20.png
--------------------------------------------------------------------------------
/platform/ios/dist/icons/icon-20@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pureqml/qmlcore/4b1eff3a3478ba8ddc51e363e99c6808212094db/platform/ios/dist/icons/icon-20@2x.png
--------------------------------------------------------------------------------
/platform/ios/dist/icons/icon-20@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pureqml/qmlcore/4b1eff3a3478ba8ddc51e363e99c6808212094db/platform/ios/dist/icons/icon-20@3x.png
--------------------------------------------------------------------------------
/platform/ios/dist/icons/icon-24@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pureqml/qmlcore/4b1eff3a3478ba8ddc51e363e99c6808212094db/platform/ios/dist/icons/icon-24@2x.png
--------------------------------------------------------------------------------
/platform/ios/dist/icons/icon-27.5@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pureqml/qmlcore/4b1eff3a3478ba8ddc51e363e99c6808212094db/platform/ios/dist/icons/icon-27.5@2x.png
--------------------------------------------------------------------------------
/platform/ios/dist/icons/icon-29.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pureqml/qmlcore/4b1eff3a3478ba8ddc51e363e99c6808212094db/platform/ios/dist/icons/icon-29.png
--------------------------------------------------------------------------------
/platform/ios/dist/icons/icon-29@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pureqml/qmlcore/4b1eff3a3478ba8ddc51e363e99c6808212094db/platform/ios/dist/icons/icon-29@2x.png
--------------------------------------------------------------------------------
/platform/ios/dist/icons/icon-29@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pureqml/qmlcore/4b1eff3a3478ba8ddc51e363e99c6808212094db/platform/ios/dist/icons/icon-29@3x.png
--------------------------------------------------------------------------------
/platform/ios/dist/icons/icon-40.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pureqml/qmlcore/4b1eff3a3478ba8ddc51e363e99c6808212094db/platform/ios/dist/icons/icon-40.png
--------------------------------------------------------------------------------
/platform/ios/dist/icons/icon-40@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pureqml/qmlcore/4b1eff3a3478ba8ddc51e363e99c6808212094db/platform/ios/dist/icons/icon-40@2x.png
--------------------------------------------------------------------------------
/platform/ios/dist/icons/icon-44@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pureqml/qmlcore/4b1eff3a3478ba8ddc51e363e99c6808212094db/platform/ios/dist/icons/icon-44@2x.png
--------------------------------------------------------------------------------
/platform/ios/dist/icons/icon-50.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pureqml/qmlcore/4b1eff3a3478ba8ddc51e363e99c6808212094db/platform/ios/dist/icons/icon-50.png
--------------------------------------------------------------------------------
/platform/ios/dist/icons/icon-50@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pureqml/qmlcore/4b1eff3a3478ba8ddc51e363e99c6808212094db/platform/ios/dist/icons/icon-50@2x.png
--------------------------------------------------------------------------------
/platform/ios/dist/icons/icon-60@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pureqml/qmlcore/4b1eff3a3478ba8ddc51e363e99c6808212094db/platform/ios/dist/icons/icon-60@2x.png
--------------------------------------------------------------------------------
/platform/ios/dist/icons/icon-60@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pureqml/qmlcore/4b1eff3a3478ba8ddc51e363e99c6808212094db/platform/ios/dist/icons/icon-60@3x.png
--------------------------------------------------------------------------------
/platform/ios/dist/icons/icon-72.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pureqml/qmlcore/4b1eff3a3478ba8ddc51e363e99c6808212094db/platform/ios/dist/icons/icon-72.png
--------------------------------------------------------------------------------
/platform/ios/dist/icons/icon-72@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pureqml/qmlcore/4b1eff3a3478ba8ddc51e363e99c6808212094db/platform/ios/dist/icons/icon-72@2x.png
--------------------------------------------------------------------------------
/platform/ios/dist/icons/icon-76.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pureqml/qmlcore/4b1eff3a3478ba8ddc51e363e99c6808212094db/platform/ios/dist/icons/icon-76.png
--------------------------------------------------------------------------------
/platform/ios/dist/icons/icon-76@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pureqml/qmlcore/4b1eff3a3478ba8ddc51e363e99c6808212094db/platform/ios/dist/icons/icon-76@2x.png
--------------------------------------------------------------------------------
/platform/ios/dist/icons/icon-83.5@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pureqml/qmlcore/4b1eff3a3478ba8ddc51e363e99c6808212094db/platform/ios/dist/icons/icon-83.5@2x.png
--------------------------------------------------------------------------------
/platform/ios/dist/icons/icon-86@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pureqml/qmlcore/4b1eff3a3478ba8ddc51e363e99c6808212094db/platform/ios/dist/icons/icon-86@2x.png
--------------------------------------------------------------------------------
/platform/ios/dist/icons/icon-98@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pureqml/qmlcore/4b1eff3a3478ba8ddc51e363e99c6808212094db/platform/ios/dist/icons/icon-98@2x.png
--------------------------------------------------------------------------------
/platform/ios/dist/icons/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pureqml/qmlcore/4b1eff3a3478ba8ddc51e363e99c6808212094db/platform/ios/dist/icons/icon.png
--------------------------------------------------------------------------------
/platform/ios/dist/icons/icon@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pureqml/qmlcore/4b1eff3a3478ba8ddc51e363e99c6808212094db/platform/ios/dist/icons/icon@2x.png
--------------------------------------------------------------------------------
/platform/ios/dist/icons/splash/Default@2x~ipad~anyany.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pureqml/qmlcore/4b1eff3a3478ba8ddc51e363e99c6808212094db/platform/ios/dist/icons/splash/Default@2x~ipad~anyany.png
--------------------------------------------------------------------------------
/platform/ios/dist/icons/splash/Default@2x~ipad~comany.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pureqml/qmlcore/4b1eff3a3478ba8ddc51e363e99c6808212094db/platform/ios/dist/icons/splash/Default@2x~ipad~comany.png
--------------------------------------------------------------------------------
/platform/ios/dist/icons/splash/Default@2x~iphone~anyany.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pureqml/qmlcore/4b1eff3a3478ba8ddc51e363e99c6808212094db/platform/ios/dist/icons/splash/Default@2x~iphone~anyany.png
--------------------------------------------------------------------------------
/platform/ios/dist/icons/splash/Default@2x~iphone~comany.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pureqml/qmlcore/4b1eff3a3478ba8ddc51e363e99c6808212094db/platform/ios/dist/icons/splash/Default@2x~iphone~comany.png
--------------------------------------------------------------------------------
/platform/ios/dist/icons/splash/Default@2x~iphone~comcom.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pureqml/qmlcore/4b1eff3a3478ba8ddc51e363e99c6808212094db/platform/ios/dist/icons/splash/Default@2x~iphone~comcom.png
--------------------------------------------------------------------------------
/platform/ios/dist/icons/splash/Default@3x~iphone~anyany.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pureqml/qmlcore/4b1eff3a3478ba8ddc51e363e99c6808212094db/platform/ios/dist/icons/splash/Default@3x~iphone~anyany.png
--------------------------------------------------------------------------------
/platform/ios/dist/icons/splash/Default@3x~iphone~anycom.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pureqml/qmlcore/4b1eff3a3478ba8ddc51e363e99c6808212094db/platform/ios/dist/icons/splash/Default@3x~iphone~anycom.png
--------------------------------------------------------------------------------
/platform/ios/dist/icons/splash/Default@3x~iphone~comany.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pureqml/qmlcore/4b1eff3a3478ba8ddc51e363e99c6808212094db/platform/ios/dist/icons/splash/Default@3x~iphone~comany.png
--------------------------------------------------------------------------------
/platform/ios/dist/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | {% block title %}{{ title }}{% endblock %}
5 |
6 | {% block head %}{% endblock %}
7 |
8 |
9 |
10 |
11 | {% block content %}{% endblock %}
12 | {% block mainscript %}
13 |
14 | {% endblock %}
15 | {% block footer %}{% endblock %}
16 |
17 |
18 |
--------------------------------------------------------------------------------
/platform/ios/video.js:
--------------------------------------------------------------------------------
1 | var Player = function(ui) {
2 | var player = ui._context.createElement('video')
3 | this.element = player
4 | this.ui = ui
5 | this.setEventListeners()
6 | this.element.style("pointer-events", "none")
7 | this.element.dom.setAttribute("webkit-playsinline", "")
8 | this.element.dom.setAttribute("playsinline", "")
9 |
10 | ui.element.remove()
11 | ui.element = player
12 | ui.parent.element.append(ui.element)
13 | }
14 |
15 | Player.prototype = Object.create(_globals.video.html5.backend.Player.prototype)
16 |
17 | exports.createPlayer = function(ui) {
18 | return new Player(ui)
19 | }
20 |
21 | exports.probeUrl = function(url) {
22 | return 150
23 | }
24 |
25 | exports.Player = Player
26 |
--------------------------------------------------------------------------------
/platform/loggerpanel/.core.js:
--------------------------------------------------------------------------------
1 | var logPanelFlag = window["$manifest$logPanel"]
2 | var logMessagesBuffer = []
3 |
4 | if (logPanelFlag) {
5 | log = function(dummy) {
6 | var args = Array.prototype.slice.call(arguments)
7 | var logger = document.getElementById("logger") || undefined
8 | if (logger) {
9 | if (logMessagesBuffer.length > 0) {
10 | for (var i = 0; i < logMessagesBuffer.length; ++i)
11 | logger.innerHTML += logMessagesBuffer[i] + "
"
12 | logMessagesBuffer = []
13 | }
14 | logger.innerHTML += args.join(" ") + "
"
15 | } else {
16 | logMessagesBuffer.push(args.join(" "))
17 | }
18 | }
19 | } else {
20 | log = console.log.bind(console)
21 | }
22 |
--------------------------------------------------------------------------------
/platform/loggerpanel/.manifest:
--------------------------------------------------------------------------------
1 | { "requires": ["html5"] }
2 |
--------------------------------------------------------------------------------
/platform/pure.blessed/.core.js:
--------------------------------------------------------------------------------
1 | if ((typeof process !== 'undefined') && (process.release.name === 'node')) {
2 | exports.core.os = process.platform
3 | exports.core.userAgent = process.release.name
4 | }
5 |
6 | _globals._backend = function() { return _globals.pure.blessed.backend }
7 | _globals.core.__locationBackend = function() { return _globals.pure.blessed.backend }
8 | _globals.core.__deviceBackend = function() { return _globals.pure.blessed.backend }
9 | _globals.core.__localStorageBackend = function() { return _globals.pure.blessed.backend }
10 |
11 | _globals.core.__videoBackends.void = function() { return _globals.pure.blessed.backend }
--------------------------------------------------------------------------------
/platform/pure.blessed/.manifest:
--------------------------------------------------------------------------------
1 | {
2 | "requires": ["pure"],
3 | "properties": {
4 | "requireExplicitRecursiveVisibilityStyle": false
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/platform/pure.femto/.core.js:
--------------------------------------------------------------------------------
1 | _globals._backend = function() { return _globals.pure.femto.backend }
2 | _globals.core.__deviceBackend = function() { return _globals.pure.femto.device }
3 | _globals.core.__locationBackend = function() { return _globals.pure.femto.location }
4 | _globals.core.__videoBackends.femto = function() { return _globals.pure.femto.video }
5 | _globals.core.__localStorageBackend = function() { return _globals.pure.femto.storage }
6 |
7 | _globals.core.os = 'android'
8 | _globals.core.browser = 'PureQML'
9 |
--------------------------------------------------------------------------------
/platform/pure.femto/.manifest:
--------------------------------------------------------------------------------
1 | {
2 | "requires": ["pure"],
3 | "export_module": true,
4 |
5 | "properties": {
6 | "style.font" : {
7 | "family": "Roboto Medium",
8 | "pixelSize": 12
9 | },
10 | "cssDisableTransformations": true
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/platform/pure.femto/backend.js:
--------------------------------------------------------------------------------
1 | exports.capabilities = {}
2 | exports._deviceInfo = {}
3 |
4 | _globals.closeApp = function() {
5 | log("closeApp")
6 | fd.closeApp()
7 | }
8 |
9 | exports.init = function(ctx) {
10 | log('backend initialization...')
11 | var options = ctx.options
12 | var nativeContext = options.nativeContext
13 |
14 | var oldOn = fd.Object.prototype.on
15 | fd.Element.prototype.on = function(name, callback) {
16 | oldOn.call(this, name, ctx.wrapNativeCallback(callback))
17 | }
18 |
19 | ctx._attachElement(nativeContext)
20 | ctx.width = nativeContext.width
21 | ctx.height = nativeContext.height
22 | nativeContext.on('resize', function(w, h) {
23 | log("resizing context to " + w + 'x' + h)
24 | ctx.system.resolutionWidth = w
25 | ctx.system.resolutionHeight = h
26 | ctx.width = w
27 | ctx.height = h
28 | })
29 | nativeContext.on('keydown', ctx.wrapNativeCallback(function(key) {
30 | var event = {
31 | timestamp: new Date().getTime()
32 | }
33 | return ctx.processKey(key, event)
34 | }))
35 | log('window size', ctx.width, ctx.height)
36 | exports._deviceInfo = fd.getDeviceInfo()
37 | }
38 |
39 |
40 | exports.run = function(ctx, callback) {
41 | ctx.system.device = exports._deviceInfo.device
42 | //schedule onload event
43 | callback()
44 | }
45 |
46 | exports.initSystem = function(system) {
47 | }
48 |
49 | exports.createElement = function(ctx, tag, cls) {
50 | var el
51 | switch(tag) {
52 | case 'input':
53 | el = new fd.Input()
54 | break
55 | case 'spinner':
56 | el = new fd.Spinner()
57 | break
58 | default:
59 | el = new fd.Element()
60 | break
61 | }
62 | if (cls)
63 | el.addClass(cls)
64 | return el
65 | }
66 |
67 | exports.initRectangle = function(rect) {
68 | rect._attachElement(new fd.Rectangle())
69 | }
70 |
71 | exports.initImage = function(image) {
72 | image._attachElement(new fd.Image())
73 | }
74 |
75 | var ImageStatusNull = 0
76 | var ImageStatusLoaded = 1
77 | var ImageStatusUnloaded = 2
78 | var ImageStatusError = 3
79 |
80 |
81 | exports.loadImage = function(image, callback) {
82 | image.status = ImageStatusNull
83 | image.element.load(image.source, callback)
84 | }
85 |
86 | exports.initText = function(text) {
87 | text._attachElement(new fd.Text())
88 | }
89 |
90 | exports.setText = function(text, html) {
91 | text.element.setText(html)
92 | }
93 |
94 | exports.layoutText = function(text) {
95 | text.element.layoutText(function(metrics) {
96 | if (metrics !== null) {
97 | text.paintedWidth = metrics.width
98 | text.paintedHeight = metrics.height
99 | } else
100 | console.log('failed to layout text', text.text)
101 | })
102 | }
103 |
104 | exports.setAnimation = function (component, name, animation) {
105 | return false
106 | }
107 |
108 | exports.requestAnimationFrame = function(callback) {
109 | return setTimeout(callback, 0)
110 | }
111 |
112 | exports.cancelAnimationFrame = function (timer) {
113 | clearTimeout(timer)
114 | }
115 |
116 | exports.tick = function(ctx) { }
117 |
118 | exports.ajax = function(ui, request) {
119 | var error = request.error, done = request.done
120 | var ctx = ui._context
121 | if (error)
122 | request.error = ctx.wrapNativeCallback(function(event) { ui.loading = false; log("Error", event); error(event); })
123 | if (done)
124 | request.done = ctx.wrapNativeCallback(function(event) { ui.loading = false; done(event); })
125 |
126 | ui.loading = true
127 | return fd.httpRequest(request)
128 | }
129 |
130 | exports.addRule = function(selector, rules) {
131 | var type = typeof rules
132 | if (type !== 'object') {
133 | log("WARNING: Stylesheet.addRule uses text based rules, it's unsupported in native clients, selector: " + selector + ", rules: " + rules)
134 | return
135 | }
136 | if ('style' in fd)
137 | fd.style(selector, rules)
138 | }
139 |
--------------------------------------------------------------------------------
/platform/pure.femto/build-android-native.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | set -euo pipefail
4 |
5 | MAIN=$0
6 |
7 | die() { echo "$*" 1>&2 ; exit 1; }
8 |
9 | if [ -z "$ANDROID_HOME" ]; then
10 | die "environment variable ANDROID_HOME=~/location/to/your/android/sdk required"
11 | fi
12 |
13 | BUILD_DIR=${PWD}/build.pure.femto.app
14 | APP_NAME=''
15 |
16 | while getopts ":a:" OPTNAME; do
17 | case ${OPTNAME} in
18 | a)
19 | APP_NAME="${OPTARG}"
20 | ;;
21 | :)
22 | die "Error: -${OPTARG} requires an argument."
23 | ;;
24 | ?)
25 | die "${MAIN} -a specify app to bundle"
26 | exit 0
27 | ;;
28 | *)
29 | echo "Invalid option ${OPTNAME}"
30 | exit 1
31 | ;;
32 | esac
33 | done
34 |
35 | export PATH=$PATH:$ANDROID_HOME/tools/
36 | export PATH=$PATH:$ANDROID_HOME/platform-tools/
37 |
38 | APP_DIR=${BUILD_DIR}/qmlcore-android
39 | if [ ! -d ${APP_DIR} ]; then
40 | echo "installing android runtime..."
41 | mkdir -p ${BUILD_DIR}
42 | pushd ${BUILD_DIR}
43 | git clone --depth=1 https://github.com/pureqml/qmlcore-android.git
44 | popd
45 | else
46 | pushd ${BUILD_DIR}
47 | git -C qmlcore-android pull --depth=1
48 | popd
49 | fi
50 |
51 | echo "compiling app..."
52 | SRC_DIR=${PWD}/build.pure.femto
53 | rm -rf ${SRC_DIR}
54 | ./qmlcore/build -j -p pure.femto ${APP_NAME}
55 | if [ ! -d ${SRC_DIR} ]; then
56 | echo "could not find project in ${SRC_DIR}"
57 | exit 1
58 | fi
59 |
60 | APP_TITLE=$(./qmlcore/build -j -p pure.femto -P title ${APP_NAME} 2>/dev/null) || die "you have to specify application title in .manifest/properties.title"
61 | APP_DOMAIN=$(./qmlcore/build -j -p pure.femto -P domain ${APP_NAME} 2>/dev/null) || die "you have to specify application domain/package in .manifest/properties.domain"
62 | APP_ICON_COLOR=$(./qmlcore/build -j -p pure.femto -P iconColor ${APP_NAME} 2>/dev/null) || APP_ICON_COLOR=""
63 | APP_SDK_VERSION=$(./qmlcore/build -j -p pure.femto -P androidtargetSdkVersion ${APP_NAME} 2>/dev/null) || APP_SDK_VERSION="29"
64 |
65 | if [ -n "${APP_NAME}" ]; then
66 | echo "using app name ${APP_NAME}..."
67 | APP_DOMAIN="${APP_DOMAIN}.${APP_NAME}"
68 | SRC_DIR="${SRC_DIR}/${APP_NAME}"
69 | else
70 | echo "using top-level build dir..."
71 | fi
72 | echo "app domain: ${APP_DOMAIN}, title: ${APP_TITLE}"
73 |
74 | echo "bundling..."
75 | DST_DIR=${BUILD_DIR}/app
76 |
77 | rm -rf ${DST_DIR}
78 | mkdir -p ${DST_DIR}
79 | git -C ${APP_DIR} checkout-index -a --prefix=${DST_DIR}/
80 |
81 | ASSETS_DIR="${DST_DIR}/app/src/main/assets"
82 | rm "${ASSETS_DIR}/.keep"
83 |
84 | pushd ${ASSETS_DIR}
85 | echo "using ${SRC_DIR} as source directory"
86 | cp -a ${SRC_DIR}/* .
87 | mv qml.*.js main.js 2>/dev/null || die "Could not find qml.*.js in build directory, in case your project has multiple app, specify the name with -a, e.g -a "
88 | if [ -e ${SRC_DIR}/icons ]; then
89 | cp -r ${SRC_DIR}/icons/* ../res/.
90 | fi
91 | popd
92 |
93 | pushd ${DST_DIR}
94 | P="s/package=\"com.pureqml.android\"/package=\"${APP_DOMAIN}\"/"
95 | sed -i "${P}" app/src/main/AndroidManifest.xml
96 | P="s/QMLCoreAndroidRuntime<\\/string>/${APP_TITLE}<\\/string>/"
97 | sed -i "${P}" app/src/main/res/values/strings.xml
98 | P="s/-keep public com\\.pureqml\\.android\\./-keep public ${APP_DOMAIN}./g"
99 | sed -i "${P}" app/proguard-rules.pro
100 | P="s/applicationId \"com\\.pureqml\\.qmlcore\\.runtime\\.android\"/applicationId \"${APP_DOMAIN}\"/"
101 | sed -i "${P}" app/build.gradle
102 | P="s/targetSdkVersion 29/targetSdkVersion ${APP_SDK_VERSION}/"
103 | sed -i "${P}" app/build.gradle
104 | P="s/compileSdkVersion 29/compileSdkVersion ${APP_SDK_VERSION}/"
105 | sed -i "${P}" app/build.gradle
106 | P="s/namespace 'com.pureqml.android'/namespace '${APP_DOMAIN}'/"
107 | sed -i "${P}" app/build.gradle
108 |
109 |
110 | if [ ! -z "$APP_ICON_COLOR" ]; then
111 | P="s/#FFFFFF/${APP_ICON_COLOR}/"
112 | sed -i "${P}" app/src/main/res/values/ic_launcher_background.xml
113 | fi
114 |
115 | JAVA_SRC=app/src/main/java/$(echo "${APP_DOMAIN}" | tr '.' '/')
116 | mkdir -p ${JAVA_SRC}
117 | mv app/src/main/java/com/pureqml/android/* ${JAVA_SRC}/
118 | rm -rf app/src/main/java/com/pureqml/android
119 | rmdir -p app/src/main/java/com/pureqml || true
120 | for J in $(find -name '*.java'); do
121 | sed -i "s/package com\\.pureqml\\.android/package ${APP_DOMAIN}/" $J
122 | sed -i "s/import com\\.pureqml\\.android/import ${APP_DOMAIN}/g" $J
123 | sed -i "s/import static com\\.pureqml\\.android/import static ${APP_DOMAIN}/g" $J
124 | done
125 | popd
126 |
127 | echo "building"
128 | pushd ${DST_DIR}
129 | TERM=xterm-color ./gradlew build #workaround grandle bug
130 | popd
131 |
132 | echo
133 | echo
134 | echo "build finished, outputting apk locations:"
135 | echo
136 | find ${BUILD_DIR} -iname '*.apk'
137 |
138 |
--------------------------------------------------------------------------------
/platform/pure.femto/device.js:
--------------------------------------------------------------------------------
1 | exports.createDevice = function(ui) {
2 | var info = ui._context.backend._deviceInfo
3 | Object.assign(ui, info)
4 |
5 | ui.lockOrientation = function(orientation) {
6 | fd.setDeviceFeature('orientation', orientation)
7 | }
8 |
9 | ui.keepScreenOn = function(enable) {
10 | fd.setDeviceFeature('keep-screen-on', enable)
11 | }
12 |
13 | ui.setFullScreen = function(enable) {
14 | fd.setDeviceFeature('fullscreen', enable)
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/platform/pure.femto/location.js:
--------------------------------------------------------------------------------
1 | var Location = function() { }
2 |
3 | exports.createLocation = function(ui) { return new Location }
4 |
5 |
--------------------------------------------------------------------------------
/platform/pure.femto/storage.js:
--------------------------------------------------------------------------------
1 | exports.createLocalStorage = function() {
2 | return new fd.LocalStorage()
3 | }
4 |
--------------------------------------------------------------------------------
/platform/pure.femto/video.js:
--------------------------------------------------------------------------------
1 | exports.createPlayer = function(ui) {
2 | log('video.createPlayer')
3 |
4 | var player = new fd.VideoPlayer()
5 |
6 | var resetState = function() {
7 | ui.ready = false
8 | ui.paused = false
9 | ui.waiting = false
10 | ui.seeking = false
11 | ui.stalled = false
12 | }
13 |
14 | player.on('stateChanged', function(state) {
15 | log('VideoPlayer: stateChanged ' + state)
16 | switch(state) {
17 | case 1:
18 | log("VideoPlayer: STATE_IDLE")
19 | resetState()
20 | break;
21 | case 2:
22 | log("VideoPlayer: STATE_BUFFERING")
23 | if (!ui.paused)
24 | ui.waiting = true
25 | break;
26 | case 3:
27 | log("VideoPlayer: STATE_READY")
28 | ui.waiting = false
29 | ui.ready = true
30 | break;
31 | case 4:
32 | log("VideoPlayer: STATE_ENDED")
33 | ui.finished()
34 | resetState()
35 | break;
36 | default:
37 | log("VideoPlayer: unhandled state", typeof state, state)
38 | }
39 | })
40 |
41 | player.on('seeked', function() {
42 | ui.waiting = false
43 | ui.seeking = false
44 | })
45 |
46 | player.on('error', function(err) {
47 | log('VideoPlayer: error: ', err)
48 | resetState(ui);
49 | ui.error({ message: err })
50 | })
51 |
52 | player.on('pause', function(isPaused) {
53 | ui.paused = isPaused
54 | log('VideoPlayer: paused: ', isPaused)
55 | })
56 |
57 | player.on('timeupdate', function(position) {
58 | ui.waiting = false
59 | ui.stalled = false
60 | if (!ui.seeking)
61 | ui.progress = position;
62 | }.bind(ui))
63 |
64 | player.on('durationchange', function(duration) {
65 | ui.duration = duration
66 | }.bind(ui))
67 |
68 |
69 | return player
70 | }
71 |
72 | exports.probeUrl = function(url) {
73 | log('video.probeUrl', url)
74 | return 150
75 | }
76 |
--------------------------------------------------------------------------------
/platform/pure.void/.core.js:
--------------------------------------------------------------------------------
1 | if ((typeof process !== 'undefined') && (process.release.name === 'node')) {
2 | exports.core.os = process.platform
3 | exports.core.userAgent = process.release.name
4 | }
5 |
6 | _globals._backend = function() { return _globals.pure.void.backend }
7 | _globals.core.__locationBackend = function() { return _globals.pure.void.backend }
8 | _globals.core.__deviceBackend = function() { return _globals.pure.void.backend }
9 | _globals.core.__localStorageBackend = function() { return _globals.pure.void.backend }
10 |
11 | _globals.core.__videoBackends.void = function() { return _globals.pure.void.backend }
--------------------------------------------------------------------------------
/platform/pure.void/.manifest:
--------------------------------------------------------------------------------
1 | { "requires": ["pure"] }
2 |
--------------------------------------------------------------------------------
/platform/pure/.core.js:
--------------------------------------------------------------------------------
1 | _globals.core.os = 'linux'
2 | _globals.core.userAgent = 'pure-native'
3 | _globals.core.language = 'en'
4 |
--------------------------------------------------------------------------------
/platform/pure/.manifest:
--------------------------------------------------------------------------------
1 | {
2 | "standalone": false,
3 | "requires": ["core"],
4 | "properties": {
5 | "cssDisableTransformations": true
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/platform/pure/Stylesheet.qml:
--------------------------------------------------------------------------------
1 | Object {
2 | constructor: {
3 | }
4 |
5 | function addRule(selector, rule) {
6 | $pure.femto.backend.addRule(selector, rule)
7 | }
8 |
9 | function allocateClass(prefix) {
10 | return prefix
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/platform/video.dashjs/.core.js:
--------------------------------------------------------------------------------
1 | _globals.core.__videoBackends.dashjs = function() { return _globals.video.dashjs.backend }
2 |
--------------------------------------------------------------------------------
/platform/video.dashjs/.manifest:
--------------------------------------------------------------------------------
1 | { "requires": ["video.html5"] }
2 |
--------------------------------------------------------------------------------
/platform/video.dashjs/backend.js:
--------------------------------------------------------------------------------
1 | var Player = function(ui) {
2 | var player = ui._context.createElement('video')
3 | player.dom.preload = "metadata"
4 | player.setAttribute("data-dashjs-player", "")
5 |
6 | this.element = player
7 | this.ui = ui
8 | this.setEventListeners()
9 |
10 | ui.element.remove()
11 | ui.element = player
12 | ui.parent.element.append(ui.element)
13 |
14 | this.dash = dashjs.MediaPlayer().create();
15 | this.dash.initialize(player.dom)
16 | }
17 |
18 | Player.prototype = Object.create(_globals.video.html5.backend.Player.prototype)
19 |
20 | Player.prototype.setSource = function(url) {
21 | log("dashjs::setSource", url)
22 | this.ui.ready = false
23 | this.dash.attachSource(url)
24 |
25 | if (this.ui.autoPlay)
26 | this.play()
27 | }
28 |
29 | Player.prototype.setupDrm = function(type, options, callback, error) {
30 | var drmConfig = {}
31 | if (type === "widevine") {
32 | drmConfig["com.widevine.alpha"] = {
33 | "serverURL": options.laServer,
34 | "systemStringPriority": ["com.widevine.something", "com.widevine.alpha"],
35 | "priority": 1
36 | }
37 | } else if (type === "playready") {
38 | drmConfig["com.microsoft.playready"] = {
39 | "serverURL": options.laServer,
40 | "priority": 1,
41 | "systemStringPriority": ["com.microsoft.playready.something", "com.microsoft.playready.recommendation", "com.microsoft.playready.hardware", "com.microsoft.playready"]
42 | }
43 | } else {
44 | error ? error(new Error("Unknown or not supported DRM type " + type)) : log("Unknown or not supported DRM type " + type)
45 | }
46 |
47 | this.dash.setProtectionData(drmConfig)
48 | if (callback)
49 | callback()
50 | }
51 |
52 |
53 | exports.createPlayer = function(ui) {
54 | return new Player(ui)
55 | }
56 |
57 | exports.probeUrl = function(url) {
58 | return 10
59 | }
60 |
61 | Player.prototype.dispose = function() {
62 | _globals.video.html5.backend.Player.prototype.dispose.apply(this)
63 | this.dash.reset()
64 | this.dash = null
65 | }
66 |
67 | exports.Player = Player
68 |
--------------------------------------------------------------------------------
/platform/video.dashjs/dist/index.html:
--------------------------------------------------------------------------------
1 | {% extends "index.html" %}
2 | {% block head %}
3 | {{ super() }}
4 |
5 | {% endblock %}
6 |
--------------------------------------------------------------------------------
/platform/video.html5/.core.js:
--------------------------------------------------------------------------------
1 | _globals.core.__videoBackends.html5 = function() { return _globals.video.html5.backend }
2 |
--------------------------------------------------------------------------------
/platform/video.html5/.manifest:
--------------------------------------------------------------------------------
1 | { "requires": ["html5"], "standalone": false }
2 |
--------------------------------------------------------------------------------
/platform/video.jsmpeg/.core.js:
--------------------------------------------------------------------------------
1 | _globals.core.__videoBackends.jsmpeg = function() { return _globals.video.jsmpeg.backend }
2 |
--------------------------------------------------------------------------------
/platform/video.jsmpeg/.manifest:
--------------------------------------------------------------------------------
1 | { "requires": ["html5"] }
2 |
--------------------------------------------------------------------------------
/platform/video.jsmpeg/dist/index.html:
--------------------------------------------------------------------------------
1 | {% extends "index.html" %}
2 | {% block head %}
3 | {{ super() }}
4 |
5 |
6 | {% endblock %}
7 |
--------------------------------------------------------------------------------
/platform/video.shaka/.core.js:
--------------------------------------------------------------------------------
1 | shaka.polyfill.installAll();
2 | if (shaka.Player.isBrowserSupported()) {
3 | _globals.core.__videoBackends.shaka = function() { return _globals.video.shaka.backend }
4 | }
5 |
--------------------------------------------------------------------------------
/platform/video.shaka/.manifest:
--------------------------------------------------------------------------------
1 | { "requires": ["video.html5"] }
2 |
--------------------------------------------------------------------------------
/platform/video.shaka/dist/index.html:
--------------------------------------------------------------------------------
1 | {% extends "index.html" %}
2 | {% block head %}
3 | {{ super() }}
4 |
5 | {% endblock %}
6 |
--------------------------------------------------------------------------------
/platform/video.videojs/.core.js:
--------------------------------------------------------------------------------
1 | _globals.core.__videoBackends.videojs = function() { return _globals.video.videojs.backend }
2 |
--------------------------------------------------------------------------------
/platform/video.videojs/.manifest:
--------------------------------------------------------------------------------
1 | { "requires": ["video.html5"] }
2 |
--------------------------------------------------------------------------------
/platform/video.videojs/backend.js:
--------------------------------------------------------------------------------
1 | var Player = function(ui) {
2 | var player = ui._context.createElement('video')
3 | player.dom.preload = "metadata"
4 |
5 | player.setAttribute('preload', 'auto')
6 | player.setAttribute('data-setup', '{}')
7 | player.setAttribute('class', 'video-js')
8 |
9 | this.element = player
10 | this.ui = ui
11 | this.setEventListeners()
12 |
13 | var uniqueId = 'videojs' + this.element._uniqueId
14 | player.setAttribute('id', uniqueId)
15 |
16 | if (ui.element)
17 | ui.element.remove()
18 | ui.element = player
19 | ui.parent.element.append(ui.element)
20 |
21 | this.videojs = window.videojs(uniqueId, { "textTrackSettings": false })
22 |
23 | this.videojs.width = 'auto'
24 | this.videojs.height = 'auto'
25 |
26 | var errorDisplay = document.getElementsByClassName("vjs-error-display")
27 | if (errorDisplay && errorDisplay.length) {
28 | for (var index = 0; index < errorDisplay.length; ++index) {
29 | errorDisplay[index].style.display = 'none'
30 | }
31 | }
32 |
33 | var videojsSpinner = document.getElementsByClassName("vjs-loading-spinner")
34 | if (videojsSpinner && videojsSpinner.length) {
35 | for (var index = 0; index < videojsSpinner.length; ++index) {
36 | videojsSpinner[index].style.display = 'none'
37 | }
38 | }
39 |
40 | var videojsControllButton = document.getElementsByClassName("vjs-control-bar")
41 | if (videojsControllButton && videojsControllButton.length) {
42 | for (var index = 0; index < videojsControllButton.length; ++index) {
43 | videojsControllButton[index].style.display = 'none'
44 | }
45 | }
46 |
47 | var videojsBigPlayButton = document.getElementsByClassName("vjs-big-play-button")
48 | if (videojsBigPlayButton && videojsBigPlayButton.length) {
49 | for (var index = 0; index < videojsBigPlayButton.length; ++index) {
50 | videojsBigPlayButton[index].style.display = 'none'
51 | }
52 | }
53 |
54 | this.videojsContaner = document.getElementById(uniqueId)
55 | this.videojsContaner.style.zindex = -1
56 | }
57 |
58 | Player.prototype = Object.create(_globals.video.html5.backend.Player.prototype)
59 |
60 | Player.prototype.dispose = function() {
61 | window.videojs(this.videojsContaner).dispose()
62 | this.videojs.dispose()
63 | this.videojs = null
64 | _globals.video.html5.backend.Player.prototype.dispose.apply(this)
65 | }
66 |
67 | Player.prototype.setSource = function(url) {
68 | var media = { 'src': url }
69 | log("SetSource", url)
70 | if (url) {
71 | var urlLower = url.toLowerCase()
72 | var querryIndex = url.indexOf("?")
73 | if (querryIndex >= 0)
74 | urlLower = urlLower.substring(0, querryIndex)
75 | var extIndex = urlLower.lastIndexOf(".")
76 | var extension = urlLower.substring(extIndex, urlLower.length)
77 | if (extension === ".m3u8" || extension === ".m3u")
78 | media.type = 'application/x-mpegURL'
79 | else if (extension === ".mpd")
80 | media.type = 'application/dash+xml'
81 | }
82 | this.videojs.src(media, { html5: { hls: { withCredentials: true } }, fluid: true, preload: 'none', techOrder: ["html5"] })
83 | if (this.ui.autoPlay)
84 | this.play()
85 | }
86 |
87 | Player.prototype.play = function() {
88 | var playPromise = this.element.dom.play()
89 | if (playPromise !== undefined) {
90 | playPromise.catch(function(e) {
91 | log('play error:', e)
92 | if (this.ui.autoPlay && e.code === DOMException.ABORT_ERR)
93 | this.element.dom.play()
94 | }.bind(this))
95 | }
96 | }
97 |
98 | Player.prototype.setRect = function(l, t, r, b) {
99 | this.videojsContaner.style.width = (r - l) + "px"
100 | this.videojsContaner.style.height = (b - t) + "px"
101 | }
102 |
103 | exports.createPlayer = function(ui) {
104 | return new Player(ui)
105 | }
106 |
107 | exports.probeUrl = function(url) {
108 | return window.videojs ? 60 : 0
109 | }
110 |
--------------------------------------------------------------------------------
/platform/video.videojs/dist/index.html:
--------------------------------------------------------------------------------
1 | {% extends "index.html" %}
2 | {% block head %}
3 | {{ super() }}
4 |
5 | {% endblock %}
6 |
--------------------------------------------------------------------------------
/platform/web.pwa/.core.js:
--------------------------------------------------------------------------------
1 | if (typeof navigator !== 'undefined' && 'serviceWorker' in navigator) {
2 | navigator.serviceWorker.register('./sw.js', {})
3 | .then((reg) => {
4 | // registration worked
5 | console.log('Registration succeeded. Scope is ' + reg.scope);
6 | }).catch((error) => {
7 | // registration failed
8 | console.log('Registration failed with ' + error);
9 | });
10 | }
11 |
--------------------------------------------------------------------------------
/platform/web.pwa/.manifest:
--------------------------------------------------------------------------------
1 | {
2 | "requires": ["web"],
3 | "templates": ["*.html", "sw.js"],
4 | "templater": "jinja2"
5 | }
6 |
7 |
--------------------------------------------------------------------------------
/platform/web.pwa/dist/sw.js:
--------------------------------------------------------------------------------
1 | var CACHE_NAME = 'pureqml-cache-v1';
2 | var urlsToCache = {{ installed_files | tojson }};
3 | urlsToCache.push('/')
4 |
5 | self.addEventListener('install', function(event) {
6 | event.waitUntil(
7 | caches.open(CACHE_NAME)
8 | .then(function(cache) {
9 | console.log('Opened cache, adding cached urls: ' + urlsToCache.length);
10 | return cache.addAll(urlsToCache);
11 | })
12 | );
13 | });
14 |
15 | self.addEventListener('fetch', function(event) {
16 | event.respondWith(
17 | caches.match(event.request)
18 | .then(function(response) {
19 | if (response) {
20 | return response;
21 | }
22 |
23 | return fetch(event.request, {credentials: 'include'}).then(
24 | function(response) {
25 | if(!response || response.status !== 200 || response.type !== 'basic') {
26 | return response;
27 | }
28 |
29 | var responseToCache = response.clone();
30 |
31 | caches.open(CACHE_NAME)
32 | .then(function(cache) {
33 | cache.put(event.request, responseToCache);
34 | });
35 |
36 | return response;
37 | });
38 | })
39 | );
40 | });
41 |
--------------------------------------------------------------------------------
/platform/web/.core.js:
--------------------------------------------------------------------------------
1 | _globals.core.__deviceBackend = function() { return _globals.web.device }
2 |
3 | var keyCodes = {
4 | 13: 'Select',
5 | 16: 'Shift',
6 | 17: 'Ctrl',
7 | 18: 'LeftAlt',
8 | 27: 'Back',
9 | 37: 'Left',
10 | 32: 'Space',
11 | 33: 'PageUp',
12 | 34: 'PageDown',
13 | 36: 'Menu',
14 | 38: 'Up',
15 | 39: 'Right',
16 | 40: 'Down',
17 | 48: '0',
18 | 49: '1',
19 | 50: '2',
20 | 51: '3',
21 | 52: '4',
22 | 53: '5',
23 | 54: '6',
24 | 55: '7',
25 | 56: '8',
26 | 57: '9',
27 | 65: 'A',
28 | 66: 'B',
29 | 67: 'C',
30 | 68: 'D',
31 | 69: 'E',
32 | 70: 'F',
33 | 71: 'G',
34 | 72: 'H',
35 | 73: 'I',
36 | 74: 'J',
37 | 75: 'K',
38 | 76: 'L',
39 | 77: 'M',
40 | 78: 'N',
41 | 79: 'O',
42 | 80: 'P',
43 | 81: 'Q',
44 | 82: 'R',
45 | 83: 'S',
46 | 84: 'T',
47 | 85: 'U',
48 | 86: 'V',
49 | 87: 'W',
50 | 88: 'X',
51 | 89: 'Y',
52 | 90: 'Z',
53 | // NumPad
54 | 96: '0',
55 | 97: '1',
56 | 98: '2',
57 | 99: '3',
58 | 100: '4',
59 | 101: '5',
60 | 102: '6',
61 | 103: '7',
62 | 104: '8',
63 | 105: '9',
64 | }
65 |
66 | if ($manifest$emulateRemoteKeys) {
67 | var emulatedKeys = {
68 | 112: 'Red',
69 | 113: 'Green',
70 | 114: 'Yellow',
71 | 115: 'Blue',
72 | 219: 'Red', // [
73 | 221: 'Green', // ]
74 | 186: 'Yellow', // ;
75 | 222: 'Blue', // '
76 | 230: 'RightAlt',
77 | 187: 'VolumeUp',
78 | 189: 'VolumeDown',
79 | 191: 'Mute',
80 | // NumPad
81 | 107: 'VolumeUp',
82 | 109: 'VolumeDown',
83 | 111: 'Mute',
84 | }
85 | for(var code in emulatedKeys) {
86 | keyCodes[code] = emulatedKeys[code]
87 | }
88 | }
89 |
90 | exports.core.keyCodes = keyCodes
91 |
92 | exports.closeApp = function() {
93 | window.close()
94 | }
95 |
--------------------------------------------------------------------------------
/platform/web/.manifest:
--------------------------------------------------------------------------------
1 | { "requires": ["video.html5"] }
2 |
--------------------------------------------------------------------------------
/platform/web/device.js:
--------------------------------------------------------------------------------
1 | var Device = function(ui) {
2 | var context = ui._context
3 | if ($manifest$system$fingerprint) {
4 | var fingerprint = new $html5.fingerprint.fingerprint.Fingerprint()
5 | context.backend.fingerprint(context, fingerprint)
6 | ui.deviceId = fingerprint.finalize()
7 | log("deviceId", ui.deviceId)
8 | } else {
9 | var deviceString = context.system.os + "_" + context.system.browser
10 | deviceString = deviceString.replace(/\s/g, '')
11 | ui.deviceId = deviceString + "_" + Math.random().toString(36).substr(2, 9)
12 | }
13 | }
14 |
15 | exports.createDevice = function(ui) {
16 | return new Device(ui)
17 | }
18 |
19 | exports.Device = Device
20 |
--------------------------------------------------------------------------------
/platform/webextension/.manifest:
--------------------------------------------------------------------------------
1 | {
2 | "requires": ["html5"],
3 | "templates": ["*.html", "manifest.json"]
4 | }
5 |
--------------------------------------------------------------------------------
/platform/webextension/dist/icon128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pureqml/qmlcore/4b1eff3a3478ba8ddc51e363e99c6808212094db/platform/webextension/dist/icon128.png
--------------------------------------------------------------------------------
/platform/webextension/dist/icon16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pureqml/qmlcore/4b1eff3a3478ba8ddc51e363e99c6808212094db/platform/webextension/dist/icon16.png
--------------------------------------------------------------------------------
/platform/webextension/dist/icon32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pureqml/qmlcore/4b1eff3a3478ba8ddc51e363e99c6808212094db/platform/webextension/dist/icon32.png
--------------------------------------------------------------------------------
/platform/webextension/dist/icon48.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pureqml/qmlcore/4b1eff3a3478ba8ddc51e363e99c6808212094db/platform/webextension/dist/icon48.png
--------------------------------------------------------------------------------
/platform/webextension/dist/index.html:
--------------------------------------------------------------------------------
1 | {% extends "index.html" %}
2 | {% block head %}
3 |
9 | {{ super() }}
10 | {% endblock %}
11 |
--------------------------------------------------------------------------------
/platform/webextension/dist/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "{{ title | default('pureqml') }}",
3 | "version": "{{ version | default('1.0.0') }}",
4 | "description": "{{ description | default('') }}",
5 | "icons": {
6 | "16": "icon16.png",
7 | "32": "icon32.png",
8 | "48": "icon48.png",
9 | "128": "icon128.png"
10 | },
11 | "browser_action": {
12 | "default_popup": "index.html"
13 | },
14 | "manifest_version": 2
15 | }
16 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | future>=0.17.1
2 | inotify>=0.2.10
3 |
--------------------------------------------------------------------------------
/test/Makefile:
--------------------------------------------------------------------------------
1 |
2 | .PHONY: check
3 | check:
4 | @./lit.py .
5 |
6 | .PHONY: clean
7 | clean:
8 | rm -rf build.* qml/Output
9 | .PHONY: superclean
10 | superclean:
11 | rm -rf .cache build.* qml/Output
12 |
--------------------------------------------------------------------------------
/test/lit.cfg.py:
--------------------------------------------------------------------------------
1 | import lit.formats
2 |
3 | config.name = "Compiler Tests"
4 | config.test_format = lit.formats.ShTest(True)
5 |
6 | config.suffixes = ['.qml']
7 |
8 | config.test_source_root = os.path.dirname(__file__)
9 | config.my_obj_root = os.path.join(config.test_source_root, "..")
10 | config.test_exec_root = os.path.join(config.my_obj_root, 'test')
11 |
12 | build_path = os.path.join(config.my_obj_root, 'build')
13 | cache_dir = os.path.join(config.test_exec_root, '.cache')
14 | manifest = '{"sources":"qml","apps":["%noext_basename_s"],"package":"test"}'
15 | config.substitutions.append(("%out","%S/../build.%basename_t"))
16 | config.substitutions.append(('%build', ": rm -f %s/test.%%noext_basename_s && rm -rf build.%%basename_t && cd %s && %s -v --build-dir=build.%%basename_t --cache-dir=%s --inline-manifest=\'%s\'" % (cache_dir, config.test_exec_root, build_path, cache_dir, manifest)))
17 |
18 |
--------------------------------------------------------------------------------
/test/lit.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | # https://medium.com/@mshockwave/using-llvm-lit-out-of-tree-5cddada85a78
4 |
5 | # To run lit-based test suite:
6 | # cd xyz/qmlcore/test && ./lit.py -va .
7 |
8 | from lit.main import main
9 | import os
10 |
11 | if __name__ == '__main__':
12 | if not os.path.exists(".cache/core.Item"):
13 | print("Note that first run may take quite a while .cache/core.* is populated...")
14 | main()
15 |
--------------------------------------------------------------------------------
/test/lit/LitTestCase.py:
--------------------------------------------------------------------------------
1 | import unittest
2 |
3 | import lit.discovery
4 | import lit.LitConfig
5 | import lit.worker
6 |
7 | """
8 | TestCase adaptor for providing a Python 'unittest' compatible interface to 'lit'
9 | tests.
10 | """
11 |
12 |
13 | class UnresolvedError(RuntimeError):
14 | pass
15 |
16 |
17 | class LitTestCase(unittest.TestCase):
18 | def __init__(self, test, lit_config):
19 | unittest.TestCase.__init__(self)
20 | self._test = test
21 | self._lit_config = lit_config
22 |
23 | def id(self):
24 | return self._test.getFullName()
25 |
26 | def shortDescription(self):
27 | return self._test.getFullName()
28 |
29 | def runTest(self):
30 | # Run the test.
31 | result = lit.worker._execute(self._test, self._lit_config)
32 |
33 | # Adapt the result to unittest.
34 | if result.code is lit.Test.UNRESOLVED:
35 | raise UnresolvedError(result.output)
36 | elif result.code.isFailure:
37 | self.fail(result.output)
38 |
39 |
40 | def load_test_suite(inputs):
41 | import platform
42 | windows = platform.system() == 'Windows'
43 |
44 | # Create the global config object.
45 | lit_config = lit.LitConfig.LitConfig(
46 | progname='lit',
47 | path=[],
48 | quiet=False,
49 | useValgrind=False,
50 | valgrindLeakCheck=False,
51 | valgrindArgs=[],
52 | noExecute=False,
53 | debug=False,
54 | isWindows=windows,
55 | params={})
56 |
57 | # Perform test discovery.
58 | tests = lit.discovery.find_tests_for_inputs(lit_config, inputs, False)
59 | test_adaptors = [LitTestCase(t, lit_config) for t in tests]
60 |
61 | # Return a unittest test suite which just runs the tests in order.
62 | return unittest.TestSuite(test_adaptors)
63 |
--------------------------------------------------------------------------------
/test/lit/ShCommands.py:
--------------------------------------------------------------------------------
1 | class Command:
2 | def __init__(self, args, redirects):
3 | self.args = list(args)
4 | self.redirects = list(redirects)
5 |
6 | def __repr__(self):
7 | return 'Command(%r, %r)' % (self.args, self.redirects)
8 |
9 | def __eq__(self, other):
10 | if not isinstance(other, Command):
11 | return False
12 |
13 | return ((self.args, self.redirects) ==
14 | (other.args, other.redirects))
15 |
16 | def toShell(self, file):
17 | for arg in self.args:
18 | if "'" not in arg:
19 | quoted = "'%s'" % arg
20 | elif '"' not in arg and '$' not in arg:
21 | quoted = '"%s"' % arg
22 | else:
23 | raise NotImplementedError('Unable to quote %r' % arg)
24 | file.write(quoted)
25 |
26 | # For debugging / validation.
27 | import ShUtil
28 | dequoted = list(ShUtil.ShLexer(quoted).lex())
29 | if dequoted != [arg]:
30 | raise NotImplementedError('Unable to quote %r' % arg)
31 |
32 | for r in self.redirects:
33 | if len(r[0]) == 1:
34 | file.write("%s '%s'" % (r[0][0], r[1]))
35 | else:
36 | file.write("%s%s '%s'" % (r[0][1], r[0][0], r[1]))
37 |
38 | class GlobItem:
39 | def __init__(self, pattern):
40 | self.pattern = pattern
41 |
42 | def __repr__(self):
43 | return self.pattern
44 |
45 | def __eq__(self, other):
46 | if not isinstance(other, Command):
47 | return False
48 |
49 | return (self.pattern == other.pattern)
50 |
51 | def resolve(self, cwd):
52 | import glob
53 | import os
54 | if os.path.isabs(self.pattern):
55 | abspath = self.pattern
56 | else:
57 | abspath = os.path.join(cwd, self.pattern)
58 | results = glob.glob(abspath)
59 | return [self.pattern] if len(results) == 0 else results
60 |
61 | class Pipeline:
62 | def __init__(self, commands, negate=False, pipe_err=False):
63 | self.commands = commands
64 | self.negate = negate
65 | self.pipe_err = pipe_err
66 |
67 | def __repr__(self):
68 | return 'Pipeline(%r, %r, %r)' % (self.commands, self.negate,
69 | self.pipe_err)
70 |
71 | def __eq__(self, other):
72 | if not isinstance(other, Pipeline):
73 | return False
74 |
75 | return ((self.commands, self.negate, self.pipe_err) ==
76 | (other.commands, other.negate, self.pipe_err))
77 |
78 | def toShell(self, file, pipefail=False):
79 | if pipefail != self.pipe_err:
80 | raise ValueError('Inconsistent "pipefail" attribute!')
81 | if self.negate:
82 | file.write('! ')
83 | for cmd in self.commands:
84 | cmd.toShell(file)
85 | if cmd is not self.commands[-1]:
86 | file.write('|\n ')
87 |
88 | class Seq:
89 | def __init__(self, lhs, op, rhs):
90 | assert op in (';', '&', '||', '&&')
91 | self.op = op
92 | self.lhs = lhs
93 | self.rhs = rhs
94 |
95 | def __repr__(self):
96 | return 'Seq(%r, %r, %r)' % (self.lhs, self.op, self.rhs)
97 |
98 | def __eq__(self, other):
99 | if not isinstance(other, Seq):
100 | return False
101 |
102 | return ((self.lhs, self.op, self.rhs) ==
103 | (other.lhs, other.op, other.rhs))
104 |
105 | def toShell(self, file, pipefail=False):
106 | self.lhs.toShell(file, pipefail)
107 | file.write(' %s\n' % self.op)
108 | self.rhs.toShell(file, pipefail)
109 |
--------------------------------------------------------------------------------
/test/lit/__init__.py:
--------------------------------------------------------------------------------
1 | """'lit' Testing Tool"""
2 |
3 | __author__ = 'Daniel Dunbar'
4 | __email__ = 'daniel@minormatter.com'
5 | __versioninfo__ = (12, 0, 0)
6 | __version__ = '.'.join(str(v) for v in __versioninfo__) + 'dev'
7 |
8 | __all__ = []
9 |
--------------------------------------------------------------------------------
/test/lit/builtin_commands/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pureqml/qmlcore/4b1eff3a3478ba8ddc51e363e99c6808212094db/test/lit/builtin_commands/__init__.py
--------------------------------------------------------------------------------
/test/lit/builtin_commands/cat.py:
--------------------------------------------------------------------------------
1 | import getopt
2 | import sys
3 | try:
4 | from StringIO import StringIO
5 | except ImportError:
6 | from io import StringIO
7 |
8 | def convertToCaretAndMNotation(data):
9 | newdata = StringIO()
10 | if isinstance(data, str):
11 | data = bytearray(data)
12 |
13 | for intval in data:
14 | if intval == 9 or intval == 10:
15 | newdata.write(chr(intval))
16 | continue
17 | if intval > 127:
18 | intval = intval -128
19 | newdata.write("M-")
20 | if intval < 32:
21 | newdata.write("^")
22 | newdata.write(chr(intval+64))
23 | elif intval == 127:
24 | newdata.write("^?")
25 | else:
26 | newdata.write(chr(intval))
27 |
28 | return newdata.getvalue().encode()
29 |
30 |
31 | def main(argv):
32 | arguments = argv[1:]
33 | short_options = "v"
34 | long_options = ["show-nonprinting"]
35 | show_nonprinting = False;
36 |
37 | try:
38 | options, filenames = getopt.gnu_getopt(arguments, short_options, long_options)
39 | except getopt.GetoptError as err:
40 | sys.stderr.write("Unsupported: 'cat': %s\n" % str(err))
41 | sys.exit(1)
42 |
43 | for option, value in options:
44 | if option == "-v" or option == "--show-nonprinting":
45 | show_nonprinting = True;
46 |
47 | writer = getattr(sys.stdout, 'buffer', None)
48 | if writer is None:
49 | writer = sys.stdout
50 | if sys.platform == "win32":
51 | import os, msvcrt
52 | msvcrt.setmode(sys.stdout.fileno(),os.O_BINARY)
53 | for filename in filenames:
54 | try:
55 | fileToCat = open(filename,"rb")
56 | contents = fileToCat.read()
57 | if show_nonprinting:
58 | contents = convertToCaretAndMNotation(contents)
59 | writer.write(contents)
60 | sys.stdout.flush()
61 | fileToCat.close()
62 | except IOError as error:
63 | sys.stderr.write(str(error))
64 | sys.exit(1)
65 |
66 | if __name__ == "__main__":
67 | main(sys.argv)
68 |
--------------------------------------------------------------------------------
/test/lit/display.py:
--------------------------------------------------------------------------------
1 | import sys
2 |
3 |
4 | def create_display(opts, tests, total_tests, workers):
5 | if opts.quiet:
6 | return NopDisplay()
7 |
8 | of_total = (' of %d' % total_tests) if (tests != total_tests) else ''
9 | header = '-- Testing: %d%s tests, %d workers --' % (tests, of_total, workers)
10 |
11 | progress_bar = None
12 | if opts.succinct and opts.useProgressBar:
13 | import lit.ProgressBar
14 | try:
15 | tc = lit.ProgressBar.TerminalController()
16 | progress_bar = lit.ProgressBar.ProgressBar(tc, header)
17 | header = None
18 | except ValueError:
19 | progress_bar = lit.ProgressBar.SimpleProgressBar('Testing: ')
20 |
21 | return Display(opts, tests, header, progress_bar)
22 |
23 |
24 | class NopDisplay(object):
25 | def print_header(self): pass
26 | def update(self, test): pass
27 | def clear(self, interrupted): pass
28 |
29 |
30 | class Display(object):
31 | def __init__(self, opts, tests, header, progress_bar):
32 | self.opts = opts
33 | self.tests = tests
34 | self.header = header
35 | self.progress_bar = progress_bar
36 | self.completed = 0
37 |
38 | def print_header(self):
39 | if self.header:
40 | print(self.header)
41 | if self.progress_bar:
42 | self.progress_bar.update(0.0, '')
43 |
44 | def update(self, test):
45 | self.completed += 1
46 |
47 | show_result = test.isFailure() or \
48 | self.opts.showAllOutput or \
49 | (not self.opts.quiet and not self.opts.succinct)
50 | if show_result:
51 | if self.progress_bar:
52 | self.progress_bar.clear(interrupted=False)
53 | self.print_result(test)
54 |
55 | if self.progress_bar:
56 | if test.isFailure():
57 | self.progress_bar.barColor = 'RED'
58 | percent = float(self.completed) / self.tests
59 | self.progress_bar.update(percent, test.getFullName())
60 |
61 | def clear(self, interrupted):
62 | if self.progress_bar:
63 | self.progress_bar.clear(interrupted)
64 |
65 | def print_result(self, test):
66 | # Show the test result line.
67 | test_name = test.getFullName()
68 | print('%s: %s (%d of %d)' % (test.result.code.name, test_name,
69 | self.completed, self.tests))
70 |
71 | # Show the test failure output, if requested.
72 | if (test.isFailure() and self.opts.showOutput) or \
73 | self.opts.showAllOutput:
74 | if test.isFailure():
75 | print("%s TEST '%s' FAILED %s" % ('*'*20, test.getFullName(),
76 | '*'*20))
77 | out = test.result.output
78 | # Encode/decode so that, when using Python 3.6.5 in Windows 10,
79 | # print(out) doesn't raise UnicodeEncodeError if out contains
80 | # special characters. However, Python 2 might try to decode
81 | # as part of the encode call if out is already encoded, so skip
82 | # encoding if it raises UnicodeDecodeError.
83 | if sys.stdout.encoding:
84 | try:
85 | out = out.encode(encoding=sys.stdout.encoding,
86 | errors="replace")
87 | except UnicodeDecodeError:
88 | pass
89 | # Python 2 can raise UnicodeDecodeError here too in cases
90 | # where the stdout encoding is ASCII. Ignore decode errors
91 | # in this case.
92 | out = out.decode(encoding=sys.stdout.encoding, errors="ignore")
93 | print(out)
94 | print("*" * 20)
95 |
96 | # Report test metrics, if present.
97 | if test.result.metrics:
98 | print("%s TEST '%s' RESULTS %s" % ('*'*10, test.getFullName(),
99 | '*'*10))
100 | items = sorted(test.result.metrics.items())
101 | for metric_name, value in items:
102 | print('%s: %s ' % (metric_name, value.format()))
103 | print("*" * 10)
104 |
105 | # Report micro-tests, if present
106 | if test.result.microResults:
107 | items = sorted(test.result.microResults.items())
108 | for micro_test_name, micro_test in items:
109 | print("%s MICRO-TEST: %s" %
110 | ('*'*3, micro_test_name))
111 |
112 | if micro_test.metrics:
113 | sorted_metrics = sorted(micro_test.metrics.items())
114 | for metric_name, value in sorted_metrics:
115 | print(' %s: %s ' % (metric_name, value.format()))
116 |
117 | # Ensure the output is flushed.
118 | sys.stdout.flush()
119 |
--------------------------------------------------------------------------------
/test/lit/formats/__init__.py:
--------------------------------------------------------------------------------
1 | from lit.formats.base import ( # noqa: F401
2 | TestFormat,
3 | FileBasedTest,
4 | OneCommandPerFileTest,
5 | ExecutableTest
6 | )
7 |
8 | from lit.formats.googletest import GoogleTest # noqa: F401
9 | from lit.formats.shtest import ShTest # noqa: F401
10 |
--------------------------------------------------------------------------------
/test/lit/formats/base.py:
--------------------------------------------------------------------------------
1 | from __future__ import absolute_import
2 | import os
3 |
4 | import lit.Test
5 | import lit.util
6 |
7 | class TestFormat(object):
8 | pass
9 |
10 | ###
11 |
12 | class FileBasedTest(TestFormat):
13 | def getTestsInDirectory(self, testSuite, path_in_suite,
14 | litConfig, localConfig):
15 | source_path = testSuite.getSourcePath(path_in_suite)
16 | for filename in os.listdir(source_path):
17 | # Ignore dot files and excluded tests.
18 | if (filename.startswith('.') or
19 | filename in localConfig.excludes):
20 | continue
21 |
22 | filepath = os.path.join(source_path, filename)
23 | if not os.path.isdir(filepath):
24 | base,ext = os.path.splitext(filename)
25 | if ext in localConfig.suffixes:
26 | yield lit.Test.Test(testSuite, path_in_suite + (filename,),
27 | localConfig)
28 |
29 | ###
30 |
31 | import re
32 | import tempfile
33 |
34 | class OneCommandPerFileTest(TestFormat):
35 | # FIXME: Refactor into generic test for running some command on a directory
36 | # of inputs.
37 |
38 | def __init__(self, command, dir, recursive=False,
39 | pattern=".*", useTempInput=False):
40 | if isinstance(command, str):
41 | self.command = [command]
42 | else:
43 | self.command = list(command)
44 | if dir is not None:
45 | dir = str(dir)
46 | self.dir = dir
47 | self.recursive = bool(recursive)
48 | self.pattern = re.compile(pattern)
49 | self.useTempInput = useTempInput
50 |
51 | def getTestsInDirectory(self, testSuite, path_in_suite,
52 | litConfig, localConfig):
53 | dir = self.dir
54 | if dir is None:
55 | dir = testSuite.getSourcePath(path_in_suite)
56 |
57 | for dirname,subdirs,filenames in os.walk(dir):
58 | if not self.recursive:
59 | subdirs[:] = []
60 |
61 | subdirs[:] = [d for d in subdirs
62 | if (d != '.svn' and
63 | d not in localConfig.excludes)]
64 |
65 | for filename in filenames:
66 | if (filename.startswith('.') or
67 | not self.pattern.match(filename) or
68 | filename in localConfig.excludes):
69 | continue
70 |
71 | path = os.path.join(dirname,filename)
72 | suffix = path[len(dir):]
73 | if suffix.startswith(os.sep):
74 | suffix = suffix[1:]
75 | test = lit.Test.Test(
76 | testSuite, path_in_suite + tuple(suffix.split(os.sep)),
77 | localConfig)
78 | # FIXME: Hack?
79 | test.source_path = path
80 | yield test
81 |
82 | def createTempInput(self, tmp, test):
83 | raise NotImplementedError('This is an abstract method.')
84 |
85 | def execute(self, test, litConfig):
86 | if test.config.unsupported:
87 | return (lit.Test.UNSUPPORTED, 'Test is unsupported')
88 |
89 | cmd = list(self.command)
90 |
91 | # If using temp input, create a temporary file and hand it to the
92 | # subclass.
93 | if self.useTempInput:
94 | tmp = tempfile.NamedTemporaryFile(suffix='.cpp')
95 | self.createTempInput(tmp, test)
96 | tmp.flush()
97 | cmd.append(tmp.name)
98 | elif hasattr(test, 'source_path'):
99 | cmd.append(test.source_path)
100 | else:
101 | cmd.append(test.getSourcePath())
102 |
103 | out, err, exitCode = lit.util.executeCommand(cmd)
104 |
105 | diags = out + err
106 | if not exitCode and not diags.strip():
107 | return lit.Test.PASS,''
108 |
109 | # Try to include some useful information.
110 | report = """Command: %s\n""" % ' '.join(["'%s'" % a
111 | for a in cmd])
112 | if self.useTempInput:
113 | report += """Temporary File: %s\n""" % tmp.name
114 | report += "--\n%s--\n""" % open(tmp.name).read()
115 | report += """Output:\n--\n%s--""" % diags
116 |
117 | return lit.Test.FAIL, report
118 |
119 |
120 | ###
121 |
122 | # Check exit code of a simple executable with no input
123 | class ExecutableTest(FileBasedTest):
124 | def execute(self, test, litConfig):
125 | if test.config.unsupported:
126 | return lit.Test.UNSUPPORTED
127 |
128 | out, err, exitCode = lit.util.executeCommand(test.getSourcePath())
129 |
130 | if not exitCode:
131 | return lit.Test.PASS, ''
132 |
133 | return lit.Test.FAIL, out+err
134 |
135 |
--------------------------------------------------------------------------------
/test/lit/formats/shtest.py:
--------------------------------------------------------------------------------
1 | from __future__ import absolute_import
2 |
3 | import lit.TestRunner
4 | import lit.util
5 |
6 | from .base import FileBasedTest
7 |
8 |
9 | class ShTest(FileBasedTest):
10 | """ShTest is a format with one file per test.
11 |
12 | This is the primary format for regression tests as described in the LLVM
13 | testing guide:
14 |
15 | http://llvm.org/docs/TestingGuide.html
16 |
17 | The ShTest files contain some number of shell-like command pipelines, along
18 | with assertions about what should be in the output.
19 | """
20 | def __init__(self, execute_external=False, extra_substitutions=[],
21 | preamble_commands=[]):
22 | self.execute_external = execute_external
23 | self.extra_substitutions = extra_substitutions
24 | self.preamble_commands = preamble_commands
25 |
26 | def execute(self, test, litConfig):
27 | return lit.TestRunner.executeShTest(test, litConfig,
28 | self.execute_external,
29 | self.extra_substitutions,
30 | self.preamble_commands)
31 |
--------------------------------------------------------------------------------
/test/lit/llvm/__init__.py:
--------------------------------------------------------------------------------
1 | from lit.llvm import config
2 |
3 | llvm_config = None
4 |
5 |
6 | def initialize(lit_config, test_config):
7 | global llvm_config
8 |
9 | llvm_config = config.LLVMConfig(lit_config, test_config)
10 |
--------------------------------------------------------------------------------
/test/lit/worker.py:
--------------------------------------------------------------------------------
1 | """
2 | The functions in this module are meant to run on a separate worker process.
3 | Exception: in single process mode _execute is called directly.
4 |
5 | For efficiency, we copy all data needed to execute all tests into each worker
6 | and store it in global variables. This reduces the cost of each task.
7 | """
8 | import contextlib
9 | import os
10 | import signal
11 | import time
12 | import traceback
13 |
14 | import lit.Test
15 | import lit.util
16 |
17 |
18 | _lit_config = None
19 | _parallelism_semaphores = None
20 |
21 |
22 | def initialize(lit_config, parallelism_semaphores):
23 | """Copy data shared by all test executions into worker processes"""
24 | global _lit_config
25 | global _parallelism_semaphores
26 | _lit_config = lit_config
27 | _parallelism_semaphores = parallelism_semaphores
28 |
29 | # We use the following strategy for dealing with Ctrl+C/KeyboardInterrupt in
30 | # subprocesses created by the multiprocessing.Pool.
31 | # https://noswap.com/blog/python-multiprocessing-keyboardinterrupt
32 | signal.signal(signal.SIGINT, signal.SIG_IGN)
33 |
34 |
35 | def execute(test):
36 | """Run one test in a multiprocessing.Pool
37 |
38 | Side effects in this function and functions it calls are not visible in the
39 | main lit process.
40 |
41 | Arguments and results of this function are pickled, so they should be cheap
42 | to copy.
43 | """
44 | with _get_parallelism_semaphore(test):
45 | result = _execute(test, _lit_config)
46 |
47 | test.setResult(result)
48 | return test
49 |
50 |
51 | # TODO(python3): replace with contextlib.nullcontext
52 | @contextlib.contextmanager
53 | def NopSemaphore():
54 | yield
55 |
56 |
57 | def _get_parallelism_semaphore(test):
58 | pg = test.config.parallelism_group
59 | if callable(pg):
60 | pg = pg(test)
61 | return _parallelism_semaphores.get(pg, NopSemaphore())
62 |
63 |
64 | # Do not inline! Directly used by LitTestCase.py
65 | def _execute(test, lit_config):
66 | start = time.time()
67 | result = _execute_test_handle_errors(test, lit_config)
68 | result.elapsed = time.time() - start
69 | result.start = start
70 | result.pid = os.getpid()
71 | return result
72 |
73 |
74 | def _execute_test_handle_errors(test, lit_config):
75 | try:
76 | result = test.config.test_format.execute(test, lit_config)
77 | return _adapt_result(result)
78 | except:
79 | if lit_config.debug:
80 | raise
81 | output = 'Exception during script execution:\n'
82 | output += traceback.format_exc()
83 | output += '\n'
84 | return lit.Test.Result(lit.Test.UNRESOLVED, output)
85 |
86 |
87 | # Support deprecated result from execute() which returned the result
88 | # code and additional output as a tuple.
89 | def _adapt_result(result):
90 | if isinstance(result, lit.Test.Result):
91 | return result
92 | assert isinstance(result, tuple)
93 | code, output = result
94 | return lit.Test.Result(code, output)
95 |
--------------------------------------------------------------------------------
/test/model.js:
--------------------------------------------------------------------------------
1 | var model = require('../core/model.js')
2 |
3 | var ModelMock = function() {
4 | this.updater = new model.ModelUpdate()
5 | this.count = 0
6 | }
7 |
8 | ModelMock.prototype.reset = function(n) {
9 | this.count = n
10 | this.updater.reset(this)
11 | }
12 |
13 | ModelMock.prototype.insert = function(begin, end) {
14 | this.count += end - begin
15 | this.updater.insert(this, begin, end)
16 | }
17 |
18 | ModelMock.prototype.remove = function(begin, end) {
19 | this.count -= end - begin
20 | this.updater.remove(this, begin, end)
21 | }
22 |
23 | ModelMock.prototype.update = function(begin, end) {
24 | this.updater.update(this, begin, end)
25 | }
26 |
27 | ModelMock.prototype.apply = function(view) {
28 | this.updater.apply(view)
29 | }
30 |
31 | module.exports = ModelMock
32 |
--------------------------------------------------------------------------------
/test/qml/enum_and_ids_in_string_context.qml:
--------------------------------------------------------------------------------
1 | // RUN: %build
2 | // RUN: grep "\this.wrap = Text.NoWrap;" %out/qml.enum_and_ids_in_string_context.js
3 | // RUN: grep "Text = _globals.core.Text.prototype" %out/qml.enum_and_ids_in_string_context.js
4 | // RUN: grep "\$this._removeUpdater('textFormat'); \$this.textFormat = Text.Html;" %out/qml.enum_and_ids_in_string_context.js
5 | // RUN: ! grep "_globals.core.Device.prototype.Platform" %out/qml.enum_and_ids_in_string_context.js
6 | // RUN: grep "this.setValue('Device.Platform', \"text.wrap\")" %out/qml.enum_and_ids_in_string_context.js
7 | Text {
8 | id: text;
9 | textFormat: Text.Html;
10 |
11 | function setValue(name, value) { }
12 |
13 | onText: {
14 | this.wrap = Text.NoWrap;
15 | this.setValue('Device.Platform', "text.wrap")
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/test/qml/expr.qml:
--------------------------------------------------------------------------------
1 | // RUN: %build
2 | // RUN: grep "this.a = (2 + (((2 \* 2) \% 3) / 4))" %out/qml.expr.js
3 | // RUN: grep "this.b = ((~ 1) - (~ 2))" %out/qml.expr.js
4 | // RUN: grep "this.c = ((2 \*\* 2) + 2)" %out/qml.expr.js
5 | // RUN: grep "this.d = ((+ 3) + (- 2))" %out/qml.expr.js
6 | // RUN: grep "this.e = (~ (~ 0))" %out/qml.expr.js
7 | // RUN: grep "this.f = ((1 + 1) << (2 + 1))" %out/qml.expr.js
8 | // RUN: grep "this.g = (1 + (2 \* (1 + 1)))" %out/qml.expr.js
9 | // RUN: grep "this.h = \[(1 \*\* 2),\$this.a,\$this.b\]" %out/qml.expr.js
10 | // RUN: grep "this.i = ((\$this.h\[0\]) + ((\$this.h\[1\]) \* (\$this.h\[2\])))" %out/qml.expr.js
11 | // RUN: grep "this.j = (123.456 . toFixed)(2)" %out/qml.expr.js
12 | // RUN: grep "this.k = ((typeof \$this.i) === 'number')" %out/qml.expr.js
13 | // RUN: grep "this.l = \$this.func((\$this.k !== undefined));" %out/qml.expr.js
14 | // RUN: grep "this.m = Date.now();" %out/qml.expr.js
15 | // RUN: grep "this.n = (new Date());" %out/qml.expr.js
16 |
17 | Object {
18 | property int a: 2 + 2 * 2 % 3 / 4;
19 | property int b: ~1 - ~2;
20 | property int c: 2 ** 2 + 2;
21 | property int d: + 3 + - 2;
22 | property int e: ~~0;
23 | property int f: 1 + 1 << 2 + 1;
24 | property int g: 1 + 2 * (1 + 1);
25 | property array h: [1 ** 2, a, b];
26 | property int i: h[0] + h[1] * h[2];
27 | property string j: (123.456).toFixed(2);
28 | property bool k: typeof i === 'number';
29 | property bool l: this.func(this.k !== undefined);
30 | property int m: Date.now();
31 | property Date n: new Date();
32 | }
33 |
--------------------------------------------------------------------------------
/test/qml/float_parse.qml:
--------------------------------------------------------------------------------
1 | // RUN: %build
2 | Text {
3 | opacity: .8;
4 | }
5 |
--------------------------------------------------------------------------------
/test/qml/gh172-a.qml:
--------------------------------------------------------------------------------
1 | // RUN: !(%build)
2 |
3 | Item {
4 | id: myItem;
5 | width: x12906efiuh;
6 | }
7 |
--------------------------------------------------------------------------------
/test/qml/gh172-b.qml:
--------------------------------------------------------------------------------
1 | // RUN: !(%build)
2 |
3 | Item {
4 | id: myItem;
5 | width: x12906efiuh.o9898dsf;
6 | }
7 |
--------------------------------------------------------------------------------
/test/qml/gh205.qml:
--------------------------------------------------------------------------------
1 | // RUN: %build
2 | // RUN: grep "delegate.width = delegate.parent.parent.w0" %out/qml.gh205.js
3 |
4 | Item {
5 | anchors.fill: context;
6 | property color defaultColor: "blue";
7 | Rectangle {
8 | anchors.fill: parent;
9 | color: parent.defaultColor;
10 | }
11 |
12 | property int w0: width/5;
13 |
14 | ListView {
15 | model: ListModel {
16 | ListElement { value: "foo"; }
17 | ListElement { value: "bar"; }
18 | ListElement { value: "baz"; }
19 | }
20 | anchors.fill: parent;
21 | delegate: Rectangle {
22 | color: defaultColor;
23 | width: w0;
24 | height: parent.parent.w0;
25 | Rectangle {
26 | anchors.margins: 10;
27 | anchors.fill: parent;
28 | color: "red";
29 | Text {
30 | anchors.centerIn: parent;
31 | color: "white";
32 | text: model.value + " " + w0;
33 | }
34 | width: 100;
35 | height: 100;
36 | }
37 | }
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/test/qml/model_row.qml:
--------------------------------------------------------------------------------
1 | // RUN: %build
2 | // RUN: grep "delegate.row = delegate._get('model')" %out/qml.model_row.js
3 |
4 | Repeater {
5 | model: ListModel {}
6 | delegate: Item {
7 | property var row: model;
8 | }
9 | }
--------------------------------------------------------------------------------
/test/qml/underscore_in_property_type.qml:
--------------------------------------------------------------------------------
1 | // RUN: %build
2 | Text {
3 | property my_special_type__ value;
4 | }
5 |
--------------------------------------------------------------------------------
/test/view.js:
--------------------------------------------------------------------------------
1 | var sinon = require('sinon')
2 |
3 | var View = function() {
4 | this._items = { length : 0 }
5 | }
6 |
7 | View.prototype._insertItems = function(begin, end) {
8 | if (begin < end)
9 | this._items.length += end - begin
10 | }
11 |
12 | View.prototype._updateItems = function(begin, end) {
13 | }
14 |
15 | View.prototype._removeItems = function(begin, end)
16 | {
17 | if (begin < end)
18 | this._items.length -= end - begin
19 | }
20 | View.prototype._updateDelegate = function(idx) { }
21 | View.prototype._updateDelegateIndex = function(idx) { }
22 |
23 | module.exports = View
24 |
--------------------------------------------------------------------------------
/update-ts:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | import argparse
4 | from compiler.ts import Ts
5 |
6 | parser = argparse.ArgumentParser()
7 | parser.add_argument("directory", nargs='+')
8 | parser.add_argument("--ts", '-t', help='tr file to write to')
9 | args = parser.parse_args()
10 |
11 | ts = Ts(args.ts)
12 | ts.scan(args.directory)
13 | ts.save()
14 |
--------------------------------------------------------------------------------