├── .gitignore ├── .travis.yml ├── CONTRIBUTING.md ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── Web.toml ├── benchmarks ├── Cargo.toml ├── build.rs └── src │ ├── main.rs │ └── utils.rs ├── build.rs ├── ci ├── install_cargo_web.sh ├── install_wasm_bindgen.sh ├── install_wasm_pack.sh └── run_tests.sh ├── examples ├── Cargo.toml ├── canvas │ ├── Cargo.toml │ ├── README.md │ ├── src │ │ └── main.rs │ └── static │ │ └── index.html ├── drag │ ├── Cargo.toml │ ├── src │ │ └── main.rs │ └── static │ │ └── index.html ├── echo │ ├── Cargo.toml │ ├── src │ │ └── main.rs │ └── static │ │ └── index.html ├── futures │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── gamepad │ ├── Cargo.toml │ ├── src │ │ └── main.rs │ └── static │ │ └── index.html ├── hasher-parcel │ ├── README.md │ ├── index.html │ ├── main.js │ ├── package-lock.json │ └── package.json ├── hasher │ ├── Cargo.toml │ ├── README.md │ ├── Web.toml │ ├── example.js │ ├── src │ │ └── lib.rs │ └── static │ │ └── index.html ├── minimal │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── todomvc │ ├── Cargo.toml │ ├── README.md │ ├── src │ │ └── main.rs │ └── static │ │ ├── css │ │ ├── todomvc-app-css │ │ │ └── index.css │ │ └── todomvc-common │ │ │ └── base.css │ │ └── index.html ├── wasm-bindgen-minimal │ ├── Cargo.toml │ ├── build.sh │ ├── index.html │ └── src │ │ └── lib.rs └── webgl │ ├── Cargo.toml │ ├── README.md │ ├── build.rs │ ├── src │ ├── main.rs │ └── webgl_rendering_context.rs │ └── static │ └── index.html ├── info └── logo.png ├── src ├── ecosystem │ ├── mod.rs │ ├── serde.rs │ └── serde_json.rs ├── lib.rs ├── webapi │ ├── array_buffer.rs │ ├── blob.rs │ ├── child_node.rs │ ├── console.rs │ ├── cross_origin_setting.rs │ ├── date.rs │ ├── document.rs │ ├── document_fragment.rs │ ├── dom_exception.rs │ ├── element.rs │ ├── error.rs │ ├── event.rs │ ├── event_target.rs │ ├── events │ │ ├── dom.rs │ │ ├── drag.rs │ │ ├── focus.rs │ │ ├── gamepad.rs │ │ ├── history.rs │ │ ├── keyboard.rs │ │ ├── mod.rs │ │ ├── mouse.rs │ │ ├── pointer.rs │ │ ├── progress.rs │ │ ├── slot.rs │ │ ├── socket.rs │ │ └── touch.rs │ ├── file.rs │ ├── file_list.rs │ ├── file_reader.rs │ ├── form_data.rs │ ├── gamepad.rs │ ├── global.rs │ ├── history.rs │ ├── html_collection.rs │ ├── html_element.rs │ ├── html_elements │ │ ├── canvas.rs │ │ ├── image.rs │ │ ├── input.rs │ │ ├── mod.rs │ │ ├── option.rs │ │ ├── select.rs │ │ ├── slot.rs │ │ ├── template.rs │ │ └── textarea.rs │ ├── location.rs │ ├── midi.rs │ ├── mod.rs │ ├── mutation_observer.rs │ ├── node.rs │ ├── node_list.rs │ ├── non_element_parent_node.rs │ ├── parent_node.rs │ ├── rendering_context.rs │ ├── selection.rs │ ├── shadow_root.rs │ ├── slotable.rs │ ├── storage.rs │ ├── string_map.rs │ ├── text_node.rs │ ├── timer_future.rs │ ├── token_list.rs │ ├── touch.rs │ ├── typed_array.rs │ ├── web_socket.rs │ ├── window.rs │ ├── window_or_worker.rs │ └── xml_http_request.rs └── webcore │ ├── array.rs │ ├── callfn.rs │ ├── discard.rs │ ├── executor.rs │ ├── ffi │ ├── emscripten.rs │ ├── mod.rs │ ├── wasm.rs │ └── wasm_bindgen.rs │ ├── global_arena.rs │ ├── initialization.rs │ ├── instance_of.rs │ ├── macros.rs │ ├── mod.rs │ ├── mutfn.rs │ ├── newtype.rs │ ├── number.rs │ ├── object.rs │ ├── once.rs │ ├── optional_arg.rs │ ├── promise.rs │ ├── promise_future.rs │ ├── reference_type.rs │ ├── serialization.rs │ ├── symbol.rs │ ├── try_from.rs │ ├── type_name.rs │ ├── unsafe_typed_array.rs │ ├── value.rs │ └── void.rs ├── standalone-tests ├── Cargo.toml ├── Web.toml ├── dependency │ ├── Cargo.toml │ └── src │ │ └── lib.rs └── src │ ├── main.rs │ ├── runtime.js │ ├── tests │ ├── mod.rs │ ├── test_derive_reference_type.rs │ ├── test_js_export.rs │ └── test_misc.rs │ └── utils.rs ├── stdweb-derive ├── Cargo.toml ├── README.md └── src │ └── lib.rs ├── stdweb-internal-macros ├── Cargo.toml ├── README.md └── src │ ├── attr_hack.rs │ ├── js_shim.rs │ ├── js_stringify.rs │ ├── lib.rs │ ├── macro_async_test.rs │ ├── macro_js.rs │ ├── macro_js_export.rs │ ├── macro_js_raw.rs │ ├── testutils.rs │ └── utils.rs ├── stdweb-internal-runtime ├── Cargo.toml ├── README.md ├── Web.toml ├── build.rs └── src │ ├── lib.rs │ ├── runtime.js │ ├── runtime_emscripten.js │ └── runtime_wasm.js └── stdweb-internal-test-macro ├── Cargo.toml └── src └── lib.rs /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | Cargo.lock 3 | node_modules 4 | 5 | .cache 6 | examples/hasher-parcel/dist 7 | *.swp 8 | rls 9 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | 3 | dist: trusty 4 | sudo: false 5 | 6 | custom: 7 | test: &test 8 | stage: test 9 | cache: 10 | cargo: true 11 | directories: 12 | - /home/travis/.local/share/cargo-web/emscripten 13 | addons: 14 | chrome: stable 15 | script: 16 | - nvm install 9 17 | - ./ci/install_cargo_web.sh 18 | - ./ci/install_wasm_bindgen.sh 19 | - ./ci/install_wasm_pack.sh 20 | - ./ci/run_tests.sh 21 | 22 | jobs: 23 | include: 24 | - <<: *test 25 | os: linux 26 | rust: nightly 27 | - <<: *test 28 | os: linux 29 | rust: stable 30 | - <<: *test 31 | os: linux 32 | rust: beta 33 | - <<: *test 34 | os: linux 35 | rust: 1.33.0 36 | env: SKIP_RUNTIME_COMPATIBILITY_CHECK=1 37 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Unless you explicitly state otherwise, any contribution intentionally submitted 2 | for inclusion in the work by you, as defined in the Apache-2.0 license, shall be 3 | dual licensed as above, without any additional terms or conditions. 4 | 5 | # Contributing to Web APIs 6 | 7 | 1. **When implementing properties and methods, follow the HTML spec** 8 | 9 | * A helpful resource for translating types from the HTML spec to Rust can be found in the `TypedArray objects` table [here](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray#TypedArray_objects) 10 | 11 | * Try to stay as close as possible to the original JS name while maintaining Rust naming conventions 12 | 13 | 2. **You can run `stdweb`'s tests with `cargo web test --features web_test`** 14 | 15 | This will run them under headless Chromium 16 | 17 | 3. **For concrete Javascript types, define a struct as an `instance_of` the concrete Js type** 18 | 19 | eg: 20 | ```rust 21 | #[derive(Clone, Debug, Eq, PartialEq, ReferenceType)] 22 | #[reference(instance_of = "CanvasGradient")] 23 | pub struct CanvasGradient(Reference); 24 | ``` 25 | 26 | 4. **Make sure to document the struct according to the documentation in MDN and provide a link** 27 | 28 | eg: 29 | ```rust 30 | /// The CanvasGradient struct represents an opaque object describing a gradient. 31 | /// It is returned by the methods CanvasRenderingContext2D.createLinearGradient() or 32 | /// CanvasRenderingContext2D.createRadialGradient(). 33 | /// 34 | /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/CanvasGradient) 35 | ``` 36 | 37 | Remember these are Rust docs so certain keywords such as `DOMString` and `Interface` need to be "translated" into Rust equivalents 38 | 39 | eg: 40 | 41 | `DOMString` -> `String`/`Enum` (whichever is more appropriate) 42 | `Interface` -> `trait` 43 | 44 | Also add a comment linking the actual HTML spec for that particular object 45 | 46 | eg: 47 | 48 | `// https://html.spec.whatwg.org/#canvasgradient` 49 | 50 | 51 | 5. **For functions that can't be overloaded properly with traits, define multiple functions with a suffix to specify their use** 52 | 53 | Try to find one "general" or "basic" function that can take the original non-suffixed name 54 | 55 | 6. **You can export structs and enums by adding them to [lib.rs](https://github.com/koute/stdweb/blob/master/src/lib.rs)** 56 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "stdweb" 3 | version = "0.4.20" 4 | authors = ["Jan Bujak "] 5 | repository = "https://github.com/koute/stdweb" 6 | homepage = "https://github.com/koute/stdweb" 7 | documentation = "https://docs.rs/stdweb/*/stdweb/" 8 | license = "MIT/Apache-2.0" 9 | readme = "README.md" 10 | keywords = ["web", "asmjs", "webasm", "javascript"] 11 | categories = ["api-bindings", "gui", "web-programming"] 12 | description = "A standard library for the client-side Web" 13 | 14 | build = "build.rs" 15 | 16 | [dependencies] 17 | discard = "1.0.3" 18 | serde = { version = "1", optional = true } 19 | serde_json = { version = "1", optional = true } 20 | futures-core-preview = { version = "0.3.0-alpha.15", optional = true } 21 | futures-channel-preview = { version = "0.3.0-alpha.15", optional = true } 22 | futures-util-preview = { version = "0.3.0-alpha.15", optional = true } 23 | futures-executor-preview = { version = "0.3.0-alpha.15", optional = true } 24 | 25 | stdweb-derive = { version = "= 0.5.3", path = "stdweb-derive" } 26 | stdweb-internal-macros = { version = "= 0.2.9", path = "stdweb-internal-macros" } 27 | stdweb-internal-runtime = { version = "0.1", path = "stdweb-internal-runtime" } 28 | 29 | [target.'cfg(all(target_arch = "wasm32", target_vendor = "unknown", target_os = "unknown", not(cargo_web)))'.dependencies] 30 | wasm-bindgen = { version = "0.2" } 31 | 32 | [dev-dependencies] 33 | serde_json = "1" 34 | serde_derive = "1" 35 | 36 | [target.'cfg(all(target_arch = "wasm32", target_vendor = "unknown", target_os = "unknown", not(cargo_web)))'.dev-dependencies] 37 | wasm-bindgen-test = "0.2" 38 | stdweb-internal-test-macro = { version = "0.1", path = "stdweb-internal-test-macro" } 39 | 40 | [build-dependencies] 41 | rustc_version = "0.2" 42 | 43 | [features] 44 | default = ["serde", "serde_json"] 45 | nightly = [] 46 | web_test = [] 47 | futures-support = ["futures-core-preview", "futures-channel-preview", "futures-util-preview", "futures-executor-preview"] 48 | experimental_features_which_may_break_on_minor_version_bumps = ["futures-support"] 49 | "docs-rs" = [] 50 | 51 | [package.metadata.docs.rs] 52 | features = ["serde", "serde_json", "futures-support", "docs-rs"] 53 | all-features = false 54 | no-default-features = true 55 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017 Jan Bujak 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /Web.toml: -------------------------------------------------------------------------------- 1 | [cargo-web] 2 | minimum-version = "0.6.24" 3 | -------------------------------------------------------------------------------- /benchmarks/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "benchmarks" 3 | version = "0.1.0" 4 | authors = ["Jan Bujak "] 5 | 6 | [dependencies] 7 | stdweb = { path = ".." } 8 | lazy_static = "1" 9 | serde = "1" 10 | serde_derive = "1" 11 | 12 | [build-dependencies] 13 | rustc_version = "0.2" 14 | -------------------------------------------------------------------------------- /benchmarks/build.rs: -------------------------------------------------------------------------------- 1 | extern crate rustc_version; 2 | 3 | use rustc_version::{version_meta, Channel}; 4 | 5 | fn main() { 6 | if version_meta().unwrap().channel == Channel::Nightly { 7 | println!( "cargo:rustc-cfg=nightly" ); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /benchmarks/src/utils.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | use std::time::Duration; 3 | 4 | pub trait Stopwatch { 5 | fn now() -> Self; 6 | fn elapsed( &self ) -> Duration; 7 | } 8 | 9 | struct PrettyDuration( Duration ); 10 | 11 | impl fmt::Display for PrettyDuration { 12 | fn fmt( &self, fmt: &mut fmt::Formatter ) -> Result< (), fmt::Error > { 13 | if self.0.as_secs() > 0 { 14 | write!( fmt, "{}.{:03}s", self.0.as_secs(), self.0.subsec_millis() ) 15 | } else if self.0.subsec_millis() > 0 { 16 | write!( fmt, "{}.{:03}ms", self.0.subsec_millis(), self.0.subsec_micros() - self.0.subsec_millis() * 1000 ) 17 | } else if self.0.subsec_micros() > 0 { 18 | write!( fmt, "{}.{:03}us", self.0.subsec_micros(), self.0.subsec_nanos() - self.0.subsec_micros() * 1000 ) 19 | } else { 20 | write!( fmt, "{}ns", self.0.subsec_nanos() ) 21 | } 22 | } 23 | } 24 | 25 | struct PrettyNumber( u64 ); 26 | 27 | impl fmt::Display for PrettyNumber { 28 | fn fmt( &self, fmt: &mut fmt::Formatter ) -> Result< (), fmt::Error > { 29 | macro_rules! emit { 30 | ($unit:expr, $divisor:expr) => { 31 | if self.0 > $divisor { 32 | return write!( fmt, "{}.{:03}{}", self.0 / $divisor, (self.0 - (self.0 / $divisor) * $divisor) / ($divisor / 1_000), $unit ); 33 | } 34 | } 35 | } 36 | 37 | emit!( "G", 1_000_000_000 ); 38 | emit!( "M", 1_000_000 ); 39 | emit!( "K", 1_000 ); 40 | write!( fmt, "{}", self.0 ) 41 | } 42 | } 43 | 44 | #[cfg(not(feature = "nightly"))] 45 | #[inline(always)] 46 | fn blackbox< T >( value: T ) -> T { 47 | use std::ptr; 48 | use std::mem; 49 | 50 | unsafe { 51 | let result = ptr::read_volatile( &value ); 52 | mem::forget( value ); 53 | result 54 | } 55 | } 56 | 57 | #[cfg(feature = "nightly")] 58 | #[inline(always)] 59 | fn blackbox< T >( value: T ) -> T { 60 | ::test::black_box( value ) 61 | } 62 | 63 | fn callibrate< S: Stopwatch, R, F: Fn() -> R >( callback: &F ) -> usize { 64 | callback(); 65 | 66 | let mut iterations_per_cycle = 1; 67 | loop { 68 | let ts = S::now(); 69 | for _ in 0..iterations_per_cycle { 70 | blackbox( callback() ); 71 | } 72 | 73 | let elapsed = ts.elapsed(); 74 | if elapsed.as_secs() as u32 * 1000 + elapsed.subsec_millis() >= 100 { 75 | break; 76 | } 77 | 78 | iterations_per_cycle *= 10; 79 | } 80 | 81 | iterations_per_cycle 82 | } 83 | 84 | fn measure< S: Stopwatch, R, F: Fn() -> R >( iterations_per_cycle: usize, callback: &F ) -> (u64, Duration) { 85 | let mut count: u64 = 0; 86 | let ts = S::now(); 87 | loop { 88 | for _ in 0..iterations_per_cycle { 89 | blackbox( callback() ); 90 | } 91 | 92 | count += iterations_per_cycle as u64; 93 | let elapsed = ts.elapsed(); 94 | if elapsed.as_secs() > 2 { 95 | return (count, elapsed); 96 | } 97 | } 98 | } 99 | 100 | pub struct BenchmarkResult { 101 | count: u64, 102 | elapsed: Duration 103 | } 104 | 105 | impl fmt::Display for BenchmarkResult { 106 | fn fmt( &self, fmt: &mut fmt::Formatter ) -> Result< (), fmt::Error > { 107 | let count = self.count; 108 | let elapsed = self.elapsed; 109 | 110 | write!( fmt, "{} per sec, {} per call, {} iterations over {}", 111 | PrettyNumber( (count as f64 / (elapsed.as_secs() as f64 + elapsed.subsec_nanos() as f64 / 1_000_000_000_f64)) as u64 ), 112 | PrettyDuration( elapsed / count as u32 ), 113 | count, 114 | PrettyDuration( elapsed ) 115 | ) 116 | } 117 | } 118 | 119 | pub fn benchmark< S: Stopwatch, R, F: Fn() -> R >( body: &F ) -> BenchmarkResult { 120 | let iterations_per_cycle = callibrate::< S, R, _ >( body ); 121 | let (count, elapsed) = measure::< S, R, _ >( iterations_per_cycle, body ); 122 | 123 | BenchmarkResult { 124 | count, elapsed 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /build.rs: -------------------------------------------------------------------------------- 1 | extern crate rustc_version; 2 | use rustc_version::{version, version_meta, Channel}; 3 | 4 | fn main() { 5 | let mut current = version().unwrap(); 6 | current.pre = Vec::new(); 7 | 8 | if version_meta().unwrap().channel == Channel::Nightly { 9 | println!( "cargo:rustc-cfg=rust_nightly" ); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /ci/install_cargo_web.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -euo pipefail 4 | IFS=$'\n\t' 5 | 6 | CARGO_WEB_RELEASE=$(curl -L -s -H 'Accept: application/json' https://github.com/koute/cargo-web/releases/latest) 7 | CARGO_WEB_VERSION=$(echo $CARGO_WEB_RELEASE | sed -e 's/.*"tag_name":"\([^"]*\)".*/\1/') 8 | CARGO_WEB_URL="https://github.com/koute/cargo-web/releases/download/$CARGO_WEB_VERSION/cargo-web-x86_64-unknown-linux-gnu.gz" 9 | 10 | echo "Downloading cargo-web from: $CARGO_WEB_URL" 11 | curl -L $CARGO_WEB_URL | gzip -d > cargo-web 12 | chmod +x cargo-web 13 | 14 | mkdir -p ~/.cargo/bin 15 | mv cargo-web ~/.cargo/bin 16 | -------------------------------------------------------------------------------- /ci/install_wasm_bindgen.sh: -------------------------------------------------------------------------------- 1 | set -euo pipefail 2 | IFS=$'\n\t' 3 | 4 | WASM_BINDGEN_RELEASE=$(curl -L -s -H 'Accept: application/json' https://github.com/rustwasm/wasm-bindgen/releases/latest) 5 | WASM_BINDGEN_VERSION=$(echo $WASM_BINDGEN_RELEASE | sed -e 's/.*"tag_name":"\([^"]*\)".*/\1/') 6 | WASM_BINDGEN_URL="https://github.com/rustwasm/wasm-bindgen/releases/download/$WASM_BINDGEN_VERSION/wasm-bindgen-$WASM_BINDGEN_VERSION-x86_64-unknown-linux-musl.tar.gz" 7 | 8 | echo "Downloading wasm-bindgen from: $WASM_BINDGEN_URL" 9 | echo $WASM_BINDGEN_URL 10 | 11 | cd /tmp 12 | 13 | curl -L $WASM_BINDGEN_URL | tar zxf - 14 | export DIR="wasm-bindgen-$WASM_BINDGEN_VERSION-x86_64-unknown-linux-musl" 15 | 16 | mkdir -p ~/.cargo/bin 17 | mv "$DIR/wasm-bindgen" ~/.cargo/bin 18 | mv "$DIR/wasm-bindgen-test-runner" ~/.cargo/bin 19 | mv "$DIR/wasm2es6js" ~/.cargo/bin 20 | -------------------------------------------------------------------------------- /ci/install_wasm_pack.sh: -------------------------------------------------------------------------------- 1 | set -euo pipefail 2 | IFS=$'\n\t' 3 | 4 | WASM_PACK_RELEASE=$(curl -L -s -H 'Accept: application/json' https://github.com/rustwasm/wasm-pack/releases/latest) 5 | WASM_PACK_VERSION=$(echo $WASM_PACK_RELEASE | sed -e 's/.*"tag_name":"\([^"]*\)".*/\1/') 6 | WASM_PACK_URL="https://github.com/rustwasm/wasm-pack/releases/download/$WASM_PACK_VERSION/wasm-pack-$WASM_PACK_VERSION-x86_64-unknown-linux-musl.tar.gz" 7 | 8 | echo "Downloading wasm-pack from: $WASM_PACK_URL" 9 | echo $WASM_PACK_URL 10 | 11 | cd /tmp 12 | 13 | curl -L $WASM_PACK_URL | tar zxf - 14 | export DIR="wasm-pack-$WASM_PACK_VERSION-x86_64-unknown-linux-musl" 15 | 16 | mkdir -p ~/.cargo/bin 17 | mv "$DIR/wasm-pack" ~/.cargo/bin 18 | -------------------------------------------------------------------------------- /ci/run_tests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -euo pipefail 4 | IFS=$'\n\t' 5 | 6 | export RUST_BACKTRACE=1 7 | 8 | CARGO_WEB=${CARGO_WEB:-cargo-web} 9 | SKIP_RUNTIME_COMPATIBILITY_CHECK=${SKIP_RUNTIME_COMPATIBILITY_CHECK:-0} 10 | 11 | set +e 12 | echo "$(rustc --version)" | grep -q "nightly" 13 | if [ "$?" = "0" ]; then 14 | export IS_NIGHTLY=1 15 | else 16 | export IS_NIGHTLY=0 17 | fi 18 | set -e 19 | 20 | echo "Is Rust from nightly: $IS_NIGHTLY" 21 | 22 | pushd stdweb-internal-macros > /dev/null 23 | cargo test 24 | popd > /dev/null 25 | 26 | # echo "Testing for wasm32-unknown-unknown (wasm-bindgen)..." 27 | # wasm-pack test --headless --chrome -- --features web_test 28 | 29 | echo "Testing for asmjs-unknown-emscripten..." 30 | $CARGO_WEB test --features web_test --target=asmjs-unknown-emscripten 31 | 32 | pushd examples/todomvc > /dev/null 33 | $CARGO_WEB build --release --target=asmjs-unknown-emscripten 34 | popd > /dev/null 35 | 36 | echo "Testing for wasm32-unknown-emscripten..." 37 | $CARGO_WEB test --features web_test --target=wasm32-unknown-emscripten 38 | 39 | pushd examples/todomvc > /dev/null 40 | $CARGO_WEB build --release --target=wasm32-unknown-emscripten 41 | popd > /dev/null 42 | 43 | echo "Testing for wasm32-unknown-unknown..." 44 | $CARGO_WEB test --nodejs --target=wasm32-unknown-unknown 45 | 46 | pushd examples/todomvc > /dev/null 47 | $CARGO_WEB build --release --target=wasm32-unknown-unknown 48 | popd > /dev/null 49 | 50 | echo "Building standalone tests..." 51 | pushd standalone-tests > /dev/null 52 | $CARGO_WEB build --release --target=wasm32-unknown-unknown 53 | 54 | echo "Running standalone tests..." 55 | node target/wasm32-unknown-unknown/release/standalone-tests.js 56 | popd > /dev/null 57 | 58 | if [ "$SKIP_RUNTIME_COMPATIBILITY_CHECK" == "0" ]; then 59 | echo "Checking whenever the old version still works with the newest runtime..." 60 | 61 | if [ ! -d target/old-version ]; then 62 | git fetch origin refs/tags/0.4.9:refs/tags/0.4.9 63 | 64 | pushd target > /dev/null 65 | git clone .. old-version 66 | popd > /dev/null 67 | fi 68 | 69 | pushd target/old-version > /dev/null 70 | git checkout 0.4.9 71 | 72 | set +e 73 | grep -q 'path = "../../stdweb-internal-runtime"' Cargo.toml 74 | if [ "$?" = "0" ]; then 75 | ALREADY_PATCHED=1 76 | else 77 | ALREADY_PATCHED=0 78 | fi 79 | set -e 80 | 81 | if [ "$ALREADY_PATCHED" = "0" ]; then 82 | sed 's/path = "stdweb-internal-runtime"/path = "..\/..\/stdweb-internal-runtime"/' -i Cargo.toml 83 | # Sanity check to make sure the replacement was done successfully. 84 | grep -q 'path = "../../stdweb-internal-runtime"' Cargo.toml 85 | fi 86 | 87 | echo "Testing old version on asmjs-unknown-emscripten..." 88 | $CARGO_WEB test --features web_test --target=asmjs-unknown-emscripten 89 | 90 | echo "The runtime is compatible!" 91 | popd > /dev/null 92 | fi 93 | 94 | NIGHTLY_EXAMPLES=() 95 | STABLE_EXAMPLES=(canvas echo minimal todomvc webgl hasher) 96 | if [ "$IS_NIGHTLY" = "1" ]; then 97 | set +u 98 | EXAMPLES=( "${NIGHTLY_EXAMPLES[@]}" "${STABLE_EXAMPLES[@]}" ) 99 | set -u 100 | else 101 | EXAMPLES=( "${STABLE_EXAMPLES[@]}" ) 102 | fi 103 | 104 | for EXAMPLE in "${EXAMPLES[@]}"; do 105 | echo "Building example: $EXAMPLE" 106 | pushd examples/$EXAMPLE > /dev/null 107 | $CARGO_WEB build 108 | popd > /dev/null 109 | echo "" 110 | done 111 | 112 | echo "Trying to run the hasher example..." 113 | pushd examples/hasher > /dev/null 114 | node example.js 115 | popd > /dev/null 116 | 117 | if [ "$IS_NIGHTLY" = "1" ]; then 118 | echo "Trying to build with parcel..." 119 | pushd examples/hasher-parcel > /dev/null 120 | npm install 121 | $(npm bin)/parcel build index.html 122 | popd > /dev/null 123 | fi 124 | -------------------------------------------------------------------------------- /examples/Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = ["canvas", "drag", "echo", "futures", "gamepad", "hasher", "minimal", "todomvc", "webgl", "wasm-bindgen-minimal"] 3 | -------------------------------------------------------------------------------- /examples/canvas/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "canvas" 3 | version = "0.1.0" 4 | authors = ["Diggory Blake"] 5 | 6 | [dependencies] 7 | stdweb = { path = "../.." } 8 | -------------------------------------------------------------------------------- /examples/canvas/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koute/stdweb/9b418d98df6fafaa4d4b87b04c304d0220292055/examples/canvas/README.md -------------------------------------------------------------------------------- /examples/canvas/src/main.rs: -------------------------------------------------------------------------------- 1 | extern crate stdweb; 2 | 3 | use stdweb::traits::*; 4 | use stdweb::unstable::TryInto; 5 | use stdweb::web::{ 6 | document, 7 | window, 8 | CanvasRenderingContext2d 9 | }; 10 | 11 | use stdweb::web::event::{ 12 | MouseMoveEvent, 13 | ResizeEvent, 14 | }; 15 | 16 | use stdweb::web::html_element::CanvasElement; 17 | 18 | // Shamelessly stolen from webplatform's TodoMVC example. 19 | macro_rules! enclose { 20 | ( ($( $x:ident ),*) $y:expr ) => { 21 | { 22 | $(let $x = $x.clone();)* 23 | $y 24 | } 25 | }; 26 | } 27 | 28 | fn main() { 29 | stdweb::initialize(); 30 | 31 | let canvas: CanvasElement = document().query_selector( "#canvas" ).unwrap().unwrap().try_into().unwrap(); 32 | let context: CanvasRenderingContext2d = canvas.get_context().unwrap(); 33 | 34 | canvas.set_width(canvas.offset_width() as u32); 35 | canvas.set_height(canvas.offset_height() as u32); 36 | 37 | window().add_event_listener( enclose!( (canvas) move |_: ResizeEvent| { 38 | canvas.set_width(canvas.offset_width() as u32); 39 | canvas.set_height(canvas.offset_height() as u32); 40 | })); 41 | 42 | canvas.add_event_listener( enclose!( (context) move |event: MouseMoveEvent| { 43 | context.fill_rect(f64::from(event.client_x() - 5), f64::from(event.client_y() - 5) 44 | , 10.0, 10.0); 45 | })); 46 | 47 | stdweb::event_loop(); 48 | } 49 | -------------------------------------------------------------------------------- /examples/canvas/static/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | stdweb • Canvas 6 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /examples/drag/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "drag" 3 | version = "0.1.0" 4 | authors = ["Willy Blandin "] 5 | 6 | [dependencies] 7 | stdweb = { path = "../.." } 8 | -------------------------------------------------------------------------------- /examples/drag/static/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | stdweb • Drag and Drop 6 | 8 | 9 | 10 |
11 |

Drag and drop characters

12 |
13 |
14 |

Team A

15 |
16 |
17 |
18 |

Team B

19 |
20 |
21 |
22 | 23 |
24 | 25 |
26 | Drop anything here (e.g. files from your file system) 27 |
28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /examples/echo/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "echo" 3 | version = "0.1.0" 4 | authors = ["Diggory Blake"] 5 | 6 | [dependencies] 7 | stdweb = { path = "../.." } 8 | -------------------------------------------------------------------------------- /examples/echo/src/main.rs: -------------------------------------------------------------------------------- 1 | extern crate stdweb; 2 | 3 | use std::rc::Rc; 4 | 5 | use stdweb::traits::*; 6 | use stdweb::unstable::TryInto; 7 | use stdweb::web::{ 8 | HtmlElement, 9 | document, 10 | WebSocket, 11 | }; 12 | 13 | use stdweb::web::event::{ 14 | KeyPressEvent, 15 | SocketOpenEvent, 16 | SocketCloseEvent, 17 | SocketErrorEvent, 18 | SocketMessageEvent, 19 | }; 20 | 21 | use stdweb::web::html_element::InputElement; 22 | 23 | // Shamelessly stolen from webplatform's TodoMVC example. 24 | macro_rules! enclose { 25 | ( ($( $x:ident ),*) $y:expr ) => { 26 | { 27 | $(let $x = $x.clone();)* 28 | $y 29 | } 30 | }; 31 | } 32 | 33 | fn main() { 34 | stdweb::initialize(); 35 | 36 | let output_div: HtmlElement = document().query_selector( ".output" ).unwrap().unwrap().try_into().unwrap(); 37 | let output_msg = Rc::new(move |msg: &str| { 38 | let elem = document().create_element("p").unwrap(); 39 | elem.set_text_content(msg); 40 | if let Some(child) = output_div.first_child() { 41 | output_div.insert_before(&elem, &child).unwrap(); 42 | } else { 43 | output_div.append_child(&elem); 44 | } 45 | }); 46 | 47 | output_msg("> Connecting..."); 48 | 49 | let ws = WebSocket::new("wss://echo.websocket.org").unwrap(); 50 | 51 | ws.add_event_listener( enclose!( (output_msg) move |_: SocketOpenEvent| { 52 | output_msg("> Opened connection"); 53 | })); 54 | 55 | ws.add_event_listener( enclose!( (output_msg) move |_: SocketErrorEvent| { 56 | output_msg("> Connection Errored"); 57 | })); 58 | 59 | ws.add_event_listener( enclose!( (output_msg) move |event: SocketCloseEvent| { 60 | output_msg(&format!("> Connection Closed: {}", event.reason())); 61 | })); 62 | 63 | ws.add_event_listener( enclose!( (output_msg) move |event: SocketMessageEvent| { 64 | output_msg(&event.data().into_text().unwrap()); 65 | })); 66 | 67 | let text_entry: InputElement = document().query_selector( ".form input" ).unwrap().unwrap().try_into().unwrap(); 68 | text_entry.add_event_listener( enclose!( (text_entry) move |event: KeyPressEvent| { 69 | if event.key() == "Enter" { 70 | event.prevent_default(); 71 | 72 | let text: String = text_entry.raw_value(); 73 | if text.is_empty() == false { 74 | text_entry.set_raw_value(""); 75 | ws.send_text(&text).unwrap(); 76 | } 77 | } 78 | })); 79 | 80 | stdweb::event_loop(); 81 | } 82 | -------------------------------------------------------------------------------- /examples/echo/static/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | stdweb • Echo 6 | 7 | 8 |
9 | 10 |
11 |
12 |
13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /examples/futures/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "futures" 3 | edition = "2018" 4 | version = "0.1.0" 5 | authors = ["Jan Bujak "] 6 | 7 | [dependencies] 8 | stdweb = { path = "../..", features = ["experimental_features_which_may_break_on_minor_version_bumps", "nightly"] } 9 | futures-preview = "0.3.0-alpha.15" 10 | -------------------------------------------------------------------------------- /examples/futures/src/main.rs: -------------------------------------------------------------------------------- 1 | #![feature(async_await)] 2 | 3 | #[macro_use] 4 | extern crate stdweb; 5 | 6 | use futures::future::{join, try_join}; 7 | use stdweb::{PromiseFuture, spawn_local, unwrap_future}; 8 | use stdweb::web::wait; 9 | use stdweb::web::error::Error; 10 | use stdweb::unstable::TryInto; 11 | 12 | 13 | // Converts a JavaScript Promise into a Rust Future 14 | fn javascript_promise() -> PromiseFuture< u32 > { 15 | js!( 16 | return new Promise( function ( success, error ) { 17 | setTimeout( function () { 18 | success( 50 ); 19 | }, 2000 ); 20 | } ); 21 | ).try_into().unwrap() 22 | } 23 | 24 | 25 | async fn print( message: &str ) { 26 | // Waits for 2000 milliseconds 27 | wait( 2000 ).await; 28 | console!( log, message ); 29 | } 30 | 31 | 32 | async fn future_main() -> Result< (), Error > { 33 | // Runs Futures synchronously 34 | print( "Hello" ).await; 35 | print( "There" ).await; 36 | 37 | { 38 | // Runs multiple Futures in parallel 39 | let ( a, b ) = join( 40 | print( "Test 1" ), 41 | print( "Test 2" ), 42 | ).await; 43 | 44 | console!( log, "join", a, b ); 45 | } 46 | 47 | { 48 | // Runs multiple Futures (which can error) in parallel 49 | let ( a, b ) = try_join( 50 | javascript_promise(), 51 | javascript_promise(), 52 | ).await?; 53 | 54 | console!( log, "try_join", a, b ); 55 | } 56 | 57 | Ok( () ) 58 | } 59 | 60 | 61 | fn main() { 62 | stdweb::initialize(); 63 | 64 | spawn_local( unwrap_future( future_main() ) ); 65 | 66 | stdweb::event_loop(); 67 | } 68 | -------------------------------------------------------------------------------- /examples/gamepad/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "gamepad" 3 | version = "0.1.0" 4 | authors = ["Cory Sherman "] 5 | 6 | [dependencies] 7 | stdweb = { path = "../.." } 8 | -------------------------------------------------------------------------------- /examples/gamepad/src/main.rs: -------------------------------------------------------------------------------- 1 | extern crate stdweb; 2 | 3 | use stdweb::traits::*; 4 | use stdweb::web::{ 5 | document, 6 | Element, 7 | Gamepad, 8 | GamepadMappingType, 9 | IEventTarget, 10 | window, 11 | }; 12 | use stdweb::web::event::{ 13 | GamepadConnectedEvent, 14 | GamepadDisconnectedEvent, 15 | IGamepadEvent, 16 | }; 17 | 18 | /// Create an element and set its content. 19 | fn elem_content(elem_type: &str, content: &str) -> Element { 20 | let elem = document().create_element(elem_type).unwrap(); 21 | elem.set_text_content(content); 22 | elem 23 | } 24 | 25 | /// Write a new line to the "log" div. 26 | fn log(msg: &str) { 27 | let log_div = document().query_selector("#log").unwrap().unwrap(); 28 | 29 | log_div.append_child(&elem_content("p", msg)); 30 | } 31 | 32 | fn get_pad_title(pad: &Gamepad) -> Element { 33 | let div = document().create_element("div").unwrap(); 34 | 35 | div.append_child(&elem_content("h2", 36 | &format!("Pad {}: {}", pad.index(), pad.id()) 37 | )); 38 | 39 | div.append_child(&elem_content("h3", 40 | if pad.connected() { "Connected" } else { "Disconnected" } 41 | )); 42 | 43 | div.append_child(&elem_content("h3", 44 | &format!("Mapping: {}", match pad.mapping() { GamepadMappingType::Standard => "standard", _ => "non-standard" }) 45 | )); 46 | 47 | div.append_child(&elem_content("h3", 48 | &format!("Last updated: {:.0}", pad.timestamp()) 49 | )); 50 | 51 | div 52 | } 53 | 54 | fn get_pad_axes(pad: &Gamepad) -> Element { 55 | let div = document().create_element("div").unwrap(); 56 | 57 | for (i, a) in pad.axes().into_iter().enumerate() { 58 | let elem = elem_content("p", 59 | &format!("Axis {}: {}", i, a) 60 | ); 61 | 62 | if a != 0.0 { 63 | elem.set_attribute("class", "gp-pressed").unwrap(); 64 | } 65 | 66 | div.append_child(&elem); 67 | } 68 | 69 | div 70 | } 71 | 72 | fn get_pad_buttons(pad: &Gamepad) -> Element { 73 | let div = document().create_element("div").unwrap(); 74 | 75 | for (i, b) in pad.buttons().into_iter().enumerate() { 76 | let elem = elem_content("p", 77 | &format!("Button {}: Pressed = {}; Value = {}", i, b.pressed(), b.value()) 78 | ); 79 | 80 | if b.value() != 0.0 { 81 | elem.set_attribute("class", "gp-pressed").unwrap(); 82 | } 83 | 84 | div.append_child(&elem); 85 | } 86 | 87 | div 88 | } 89 | 90 | fn get_pad_state(pad: &Option) -> Element { 91 | let elem = document().create_element("div").unwrap(); 92 | 93 | match pad { 94 | Some(pad) => { 95 | elem.append_child(&get_pad_title(&pad)); 96 | elem.append_child(&get_pad_axes(&pad)); 97 | elem.append_child(&get_pad_buttons(&pad)); 98 | }, 99 | None => { 100 | elem.append_child(&elem_content("h2", "No pad")); 101 | } 102 | } 103 | 104 | elem 105 | } 106 | 107 | /// Update gamepad state view 108 | fn animate() { 109 | let list = document().create_element("ul").unwrap(); 110 | 111 | for pad in Gamepad::get_all() { 112 | let item = document().create_element("li").unwrap(); 113 | item.append_child(&get_pad_state(&pad)); 114 | list.append_child(&item); 115 | } 116 | 117 | let state = document().query_selector("#state").unwrap().unwrap(); 118 | 119 | state.set_text_content(""); 120 | state.append_child(&list); 121 | 122 | // queue another animate() on the next frame 123 | window().request_animation_frame(|_| animate()); 124 | } 125 | 126 | fn main() { 127 | stdweb::initialize(); 128 | 129 | log("Waiting for gamepad connection..."); 130 | 131 | animate(); 132 | 133 | window().add_event_listener( move |e: GamepadConnectedEvent| { 134 | let pad = e.gamepad(); 135 | let message = format!("gamepad \"{}\" connected", pad.id()); 136 | log(&message); 137 | }); 138 | 139 | window().add_event_listener( move |e: GamepadDisconnectedEvent| { 140 | let pad = e.gamepad(); 141 | let message = format!("gamepad \"{}\" disconnected", pad.id()); 142 | log(&message); 143 | }); 144 | 145 | stdweb::event_loop(); 146 | } 147 | -------------------------------------------------------------------------------- /examples/gamepad/static/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | stdweb • Gamepad 6 | 12 | 13 | 14 |

Gamepad State

15 |
16 |
17 |

Connection Log

18 |
19 |
20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /examples/hasher-parcel/README.md: -------------------------------------------------------------------------------- 1 | This example shows how to integrate Rust with the [Parcel] bundler. 2 | 3 | Please see the `hasher` example first for how to export methods from Rust 4 | so that they're callable on the JavaScript side. 5 | 6 | [Parcel]: https://parceljs.org/ 7 | 8 | ### Running the demo 9 | 10 | 1. Install the dependencies: 11 | 12 | $ npm install 13 | 14 | 2. Start the demo: 15 | 16 | $ $(npm bin)/parcel index.html 17 | 18 | 3. Visit `http://localhost:1234` with your browser. 19 | 20 | ### How does this work? 21 | 22 | This uses our [Parcel plugin] to integrate `cargo-web` with Parcel. 23 | 24 | If you have an existing Parcel project you can simply add our plugin: 25 | 26 | $ npm install --save parcel-plugin-cargo-web 27 | 28 | and then just import the `Cargo.toml` of a Rust crate, where in this 29 | case the whole code looks like this: 30 | 31 | ```js 32 | import hasher from "../hasher/Cargo.toml"; 33 | 34 | var input = document.getElementById( "input" ); 35 | var output = document.getElementById( "output" ); 36 | output.innerText = hasher.sha1( input.value ); 37 | 38 | input.addEventListener( "keyup", function( event ) { 39 | output.innerText = hasher.sha1( input.value ); 40 | }); 41 | ``` 42 | 43 | [Parcel plugin]: https://github.com/koute/parcel-plugin-cargo-web 44 | -------------------------------------------------------------------------------- /examples/hasher-parcel/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | stdweb • Hasher (Parcel) 6 | 7 | 8 |
9 | 10 |
11 |

Digest:

12 | Digest will be shown here 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /examples/hasher-parcel/main.js: -------------------------------------------------------------------------------- 1 | import hasher from "../hasher/Cargo.toml"; 2 | 3 | var input = document.getElementById( "input" ); 4 | var output = document.getElementById( "output" ); 5 | output.innerText = hasher.sha1( input.value ); 6 | 7 | input.addEventListener( "keyup", function( event ) { 8 | output.innerText = hasher.sha1( input.value ); 9 | }); 10 | -------------------------------------------------------------------------------- /examples/hasher-parcel/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hasher-parcel", 3 | "version": "0.1.0", 4 | "description": "", 5 | "scripts": { 6 | "test": "echo \"Error: no test specified\" && exit 1" 7 | }, 8 | "author": "", 9 | "license": "(MIT OR Apache-2.0)", 10 | "dependencies": { 11 | "parcel-bundler": "^1.9", 12 | "parcel-plugin-cargo-web": "^0.3.0" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /examples/hasher/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "hasher" 3 | version = "0.1.0" 4 | authors = ["Jan Bujak "] 5 | 6 | [lib] 7 | crate-type = ["cdylib"] 8 | 9 | [dependencies] 10 | stdweb = { path = "../.." } 11 | sha1 = "0.3" 12 | -------------------------------------------------------------------------------- /examples/hasher/README.md: -------------------------------------------------------------------------------- 1 | This example shows how to export methods from Rust and call 2 | them from JavaScript, either in the browser or from Nodejs. 3 | 4 | ***WARNING***: This is only supported for Rust's native `wasm32-unknown-unknown` target on Rust nightly! 5 | 6 | ### Running the demo (browser) 7 | 8 | 1. Start the demo: 9 | 10 | $ cargo-web start 11 | 12 | 2. Visit `http://localhost:8000` with your browser. 13 | 14 | ### Running the demo (nodejs) 15 | 16 | 1. Build the example: 17 | 18 | $ cargo-web build 19 | 20 | 2. Run it: 21 | 22 | $ node example.js 23 | 24 | ### How does this work? 25 | 26 | `stdweb` exports a procedural attribute macro called `js_export` 27 | which can be used to mark arbitrary functions for export, e.g.: 28 | 29 | ```rust 30 | #[macro_use] 31 | extern crate stdweb; 32 | extern crate sha1; 33 | 34 | use stdweb::js_export; 35 | use sha1::Sha1; 36 | 37 | #[js_export] 38 | fn sha1( string: String ) -> String { 39 | let mut hasher = Sha1::new(); 40 | hasher.update( string.as_bytes() ); 41 | hasher.digest().to_string() 42 | } 43 | ``` 44 | 45 | This supports almost every type you can pass through the `js!` macro, 46 | which includes objects, arrays, arbitrary DOM types, etc. 47 | 48 | A current limitation of the `#[js_export]` is that all of the functions 49 | you want to export must be defined in your `lib.rs`, or alternatively 50 | they can be defined in another file, but you'll have to import them 51 | with `pub use another_module::*` into your `lib.rs`. 52 | 53 | If you compile this code with `cargo web build --target=wasm32-unknown-unknown` you'll get two files: 54 | 55 | * `target/wasm32-unknown-unknown/debug/hasher.js` 56 | * `target/wasm32-unknown-unknown/debug/hasher.wasm` 57 | 58 | You can copy them into your JavaScript project and load like any other JavaScript file: 59 | 60 | ```html 61 | 62 | ``` 63 | 64 | After it's loaded you can access `Rust.hasher`, which is a [Promise] that 65 | will be resolved once the WebAssembly module is loaded. Inside that promise 66 | you'll find everything which you've marked with `#[js_export]`: 67 | 68 | ```html 69 | 77 | ``` 78 | 79 | You can also use the very same `hasher.js` from Nodejs: 80 | 81 | ```js 82 | const hasher = require( "hasher.js" ); 83 | 84 | const string = "fiddlesticks"; 85 | const hash = hasher.sha1( string ); 86 | 87 | console.log( "Hash of " + string + " is '" + hash + "'" ); 88 | ``` 89 | 90 | For the Nodejs environment the WebAssembly is compiled synchronously. 91 | 92 | [Promise]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise 93 | 94 | ### Integration with JavaScript bundlers 95 | 96 | You can take a look at the `hasher-parcel` example for how to integrate 97 | with the [Parcel] bundler. 98 | 99 | [Parcel]: https://parceljs.org/ 100 | -------------------------------------------------------------------------------- /examples/hasher/Web.toml: -------------------------------------------------------------------------------- 1 | default-target = "wasm32-unknown-unknown" 2 | -------------------------------------------------------------------------------- /examples/hasher/example.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const hasher = require( './../target/wasm32-unknown-unknown/debug/hasher.js' ); 4 | 5 | const string = "fiddlesticks"; 6 | const hash = hasher.sha1( string ); 7 | 8 | console.log( "Hash of " + string + " is '" + hash + "'" ); 9 | -------------------------------------------------------------------------------- /examples/hasher/src/lib.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate stdweb; 3 | extern crate sha1; 4 | 5 | use stdweb::js_export; 6 | use sha1::Sha1; 7 | 8 | #[js_export] 9 | fn sha1( string: &str ) -> String { 10 | let mut hasher = Sha1::new(); 11 | hasher.update( string.as_bytes() ); 12 | hasher.digest().to_string() 13 | } 14 | -------------------------------------------------------------------------------- /examples/hasher/static/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | stdweb • Hasher 6 | 7 | 8 |
9 | 10 |
11 |

Digest:

12 | Digest will be shown here 13 | 14 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /examples/minimal/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "minimal" 3 | version = "0.1.0" 4 | authors = ["Jan Bujak "] 5 | 6 | [dependencies] 7 | stdweb = { path = "../.." } 8 | -------------------------------------------------------------------------------- /examples/minimal/src/main.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate stdweb; 3 | 4 | fn main() { 5 | stdweb::initialize(); 6 | 7 | let message = "Hello, 世界!"; 8 | js! { 9 | alert( @{message} ); 10 | } 11 | 12 | stdweb::event_loop(); 13 | } 14 | -------------------------------------------------------------------------------- /examples/todomvc/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "todomvc" 3 | version = "0.1.0" 4 | authors = ["Jan Bujak "] 5 | 6 | [dependencies] 7 | serde = "1" 8 | serde_json = "1" 9 | serde_derive = "1" 10 | stdweb = { path = "../.." } 11 | -------------------------------------------------------------------------------- /examples/todomvc/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koute/stdweb/9b418d98df6fafaa4d4b87b04c304d0220292055/examples/todomvc/README.md -------------------------------------------------------------------------------- /examples/todomvc/static/css/todomvc-common/base.css: -------------------------------------------------------------------------------- 1 | hr { 2 | margin: 20px 0; 3 | border: 0; 4 | border-top: 1px dashed #c5c5c5; 5 | border-bottom: 1px dashed #f7f7f7; 6 | } 7 | 8 | .learn a { 9 | font-weight: normal; 10 | text-decoration: none; 11 | color: #b83f45; 12 | } 13 | 14 | .learn a:hover { 15 | text-decoration: underline; 16 | color: #787e7e; 17 | } 18 | 19 | .learn h3, 20 | .learn h4, 21 | .learn h5 { 22 | margin: 10px 0; 23 | font-weight: 500; 24 | line-height: 1.2; 25 | color: #000; 26 | } 27 | 28 | .learn h3 { 29 | font-size: 24px; 30 | } 31 | 32 | .learn h4 { 33 | font-size: 18px; 34 | } 35 | 36 | .learn h5 { 37 | margin-bottom: 0; 38 | font-size: 14px; 39 | } 40 | 41 | .learn ul { 42 | padding: 0; 43 | margin: 0 0 30px 25px; 44 | } 45 | 46 | .learn li { 47 | line-height: 20px; 48 | } 49 | 50 | .learn p { 51 | font-size: 15px; 52 | font-weight: 300; 53 | line-height: 1.3; 54 | margin-top: 0; 55 | margin-bottom: 0; 56 | } 57 | 58 | #issue-count { 59 | display: none; 60 | } 61 | 62 | .quote { 63 | border: none; 64 | margin: 20px 0 60px 0; 65 | } 66 | 67 | .quote p { 68 | font-style: italic; 69 | } 70 | 71 | .quote p:before { 72 | content: '“'; 73 | font-size: 50px; 74 | opacity: .15; 75 | position: absolute; 76 | top: -20px; 77 | left: 3px; 78 | } 79 | 80 | .quote p:after { 81 | content: '”'; 82 | font-size: 50px; 83 | opacity: .15; 84 | position: absolute; 85 | bottom: -42px; 86 | right: 3px; 87 | } 88 | 89 | .quote footer { 90 | position: absolute; 91 | bottom: -40px; 92 | right: 0; 93 | } 94 | 95 | .quote footer img { 96 | border-radius: 3px; 97 | } 98 | 99 | .quote footer a { 100 | margin-left: 5px; 101 | vertical-align: middle; 102 | } 103 | 104 | .speech-bubble { 105 | position: relative; 106 | padding: 10px; 107 | background: rgba(0, 0, 0, .04); 108 | border-radius: 5px; 109 | } 110 | 111 | .speech-bubble:after { 112 | content: ''; 113 | position: absolute; 114 | top: 100%; 115 | right: 30px; 116 | border: 13px solid transparent; 117 | border-top-color: rgba(0, 0, 0, .04); 118 | } 119 | 120 | .learn-bar > .learn { 121 | position: absolute; 122 | width: 272px; 123 | top: 8px; 124 | left: -300px; 125 | padding: 10px; 126 | border-radius: 5px; 127 | background-color: rgba(255, 255, 255, .6); 128 | transition-property: left; 129 | transition-duration: 500ms; 130 | } 131 | 132 | @media (min-width: 899px) { 133 | .learn-bar { 134 | width: auto; 135 | padding-left: 300px; 136 | } 137 | 138 | .learn-bar > .learn { 139 | left: 8px; 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /examples/todomvc/static/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | stdweb • TodoMVC 6 | 7 | 8 | 9 | 10 |
11 |
12 |

todos

13 | 14 |
15 |
16 | 17 | 18 |
    19 |
    20 | 21 | 32 | 33 |
    34 |
    35 |
    36 |
    37 |

    Double-click to edit a todo

    38 |

    Part of the stdweb project

    39 |

    Based on TodoMVC

    40 |
    41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /examples/wasm-bindgen-minimal/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "wasm-bindgen-minimal" 3 | version = "0.1.0" 4 | authors = ["Jan Bujak "] 5 | edition = "2018" 6 | 7 | [lib] 8 | crate-type = ["cdylib"] 9 | 10 | [dependencies] 11 | wasm-bindgen = "0.2" 12 | stdweb = { path = "../.." } 13 | -------------------------------------------------------------------------------- /examples/wasm-bindgen-minimal/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -ex 4 | 5 | wasm-pack build --target web 6 | python3 -m http.server 7 | -------------------------------------------------------------------------------- /examples/wasm-bindgen-minimal/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /examples/wasm-bindgen-minimal/src/lib.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate stdweb; 3 | 4 | use wasm_bindgen::prelude::*; 5 | 6 | #[wasm_bindgen(start)] 7 | pub fn main() -> Result<(), JsValue> { 8 | let message = "Hello, 世界!"; 9 | js! { 10 | alert( @{message} ); 11 | } 12 | 13 | Ok(()) 14 | } 15 | -------------------------------------------------------------------------------- /examples/webgl/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "webgl" 3 | version = "0.1.0" 4 | authors = ["Diggory Blake"] 5 | 6 | [dependencies] 7 | stdweb = { path = "../.." } 8 | stdweb-derive = { path = "../../stdweb-derive" } 9 | serde = "1.0.0" 10 | serde_derive = "1.0.0" 11 | 12 | [build-dependencies] 13 | webgl_generator = "0.1.0" 14 | -------------------------------------------------------------------------------- /examples/webgl/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koute/stdweb/9b418d98df6fafaa4d4b87b04c304d0220292055/examples/webgl/README.md -------------------------------------------------------------------------------- /examples/webgl/build.rs: -------------------------------------------------------------------------------- 1 | extern crate webgl_generator; 2 | 3 | use webgl_generator::*; 4 | use std::env; 5 | use std::fs::File; 6 | use std::path::*; 7 | 8 | fn main() { 9 | let dest = env::var("OUT_DIR").unwrap(); 10 | let mut file = File::create(&Path::new(&dest).join("webgl_rendering_context.rs")).unwrap(); 11 | 12 | Registry::new(Api::WebGl2, Exts::NONE) 13 | .write_bindings(StdwebGenerator, &mut file) 14 | .unwrap(); 15 | } 16 | -------------------------------------------------------------------------------- /examples/webgl/src/webgl_rendering_context.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code, unused_parens, unused_imports)] 2 | 3 | include!(concat!(env!("OUT_DIR"), "/webgl_rendering_context.rs")); 4 | -------------------------------------------------------------------------------- /examples/webgl/static/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | stdweb • WebGL 6 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /info/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koute/stdweb/9b418d98df6fafaa4d4b87b04c304d0220292055/info/logo.png -------------------------------------------------------------------------------- /src/ecosystem/mod.rs: -------------------------------------------------------------------------------- 1 | #[cfg(feature = "serde")] 2 | pub mod serde; 3 | 4 | #[cfg(feature = "serde_json")] 5 | pub mod serde_json; 6 | -------------------------------------------------------------------------------- /src/ecosystem/serde_json.rs: -------------------------------------------------------------------------------- 1 | use std::collections::BTreeMap; 2 | use serde_json::value::Value as JsonValue; 3 | use webcore::value::Value; 4 | use webcore::try_from::{TryFrom, TryInto}; 5 | use webcore::number::ConversionError; 6 | 7 | impl TryFrom< JsonValue > for Value { 8 | type Error = ConversionError; 9 | 10 | #[inline] 11 | fn try_from( value: JsonValue ) -> Result< Self, Self::Error > { 12 | let result = match value { 13 | JsonValue::Null => Value::Null, 14 | JsonValue::Bool( value ) => Value::Bool( value ), 15 | JsonValue::Number( value ) => { 16 | if let Some( value ) = value.as_u64() { 17 | Value::Number( value.try_into()? ) 18 | } else if let Some( value ) = value.as_i64() { 19 | Value::Number( value.try_into()? ) 20 | } else { 21 | Value::Number( value.as_f64().unwrap().into() ) 22 | } 23 | }, 24 | JsonValue::String( value ) => Value::String( value ), 25 | JsonValue::Array( value ) => { 26 | let mut vector: Vec< Value > = Vec::new(); 27 | 28 | vector.reserve( value.len() ); 29 | for element in value.into_iter() { 30 | vector.push( element.try_into()? ); 31 | } 32 | 33 | vector.into() 34 | }, 35 | JsonValue::Object( value ) => { 36 | let mut map: BTreeMap< String, Value > = BTreeMap::new(); 37 | for (key, value) in value.into_iter() { 38 | map.insert( key.into(), value.try_into()? ); 39 | } 40 | 41 | map.into() 42 | } 43 | }; 44 | 45 | Ok( result ) 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/webapi/array_buffer.rs: -------------------------------------------------------------------------------- 1 | use webcore::value::Reference; 2 | use webcore::try_from::TryInto; 3 | use webcore::value::Value; 4 | use webapi::typed_array::TypedArray; 5 | use private::TODO; 6 | 7 | /// The `ArrayBuffer` object is used to represent a generic, fixed-length raw binary data buffer. 8 | /// You cannot directly manipulate the contents of an ArrayBuffer; instead, you create an [TypedArray](struct.TypedArray.html) 9 | /// to do it. 10 | /// 11 | /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer) 12 | // https://www.ecma-international.org/ecma-262/6.0/#sec-arraybuffer-constructor 13 | #[derive(Clone, Debug, PartialEq, Eq, ReferenceType)] 14 | #[reference(instance_of = "ArrayBuffer")] 15 | pub struct ArrayBuffer( Reference ); 16 | 17 | impl ArrayBuffer { 18 | /// Creates a new `ArrayBuffer` with the given length in bytes. 19 | // https://www.ecma-international.org/ecma-262/6.0/#sec-arraybuffer-length 20 | pub fn new( length: u64 ) -> Result< Self, TODO > { 21 | let length: Value = length.try_into().unwrap(); 22 | Ok( js!( return new ArrayBuffer( @{length} ); ).try_into().unwrap() ) 23 | } 24 | 25 | /// Returns the length of the buffer, in bytes. 26 | // https://www.ecma-international.org/ecma-262/6.0/#sec-get-arraybuffer.prototype.bytelength 27 | pub fn len( &self ) -> u64 { 28 | let reference = self.as_ref(); 29 | let length = js!( return @{reference}.byteLength; ).try_into().unwrap(); 30 | length 31 | } 32 | } 33 | 34 | // TODO: Implement for other types. 35 | // TODO: Implement slightly more efficiently than going through the TypedArray. 36 | impl From< ArrayBuffer > for Vec< u8 > { 37 | fn from( buffer: ArrayBuffer ) -> Self { 38 | let typed_array: TypedArray< u8 > = buffer.into(); 39 | typed_array.into() 40 | } 41 | } 42 | 43 | impl< 'a > From< &'a ArrayBuffer > for Vec< u8 > { 44 | fn from( buffer: &'a ArrayBuffer ) -> Self { 45 | let typed_array: TypedArray< u8 > = buffer.into(); 46 | typed_array.into() 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/webapi/blob.rs: -------------------------------------------------------------------------------- 1 | use std::ops::{RangeBounds, Bound}; 2 | 3 | use webcore::value::Reference; 4 | use webcore::try_from::TryInto; 5 | use webcore::reference_type::ReferenceType; 6 | use webcore::number::Number; 7 | use webcore::optional_arg::OptionalArg; 8 | 9 | // https://w3c.github.io/FileAPI/#ref-for-dfn-slice 10 | fn slice_blob< T, U >( blob: &T, range: U, content_type: Option< &str > ) -> Blob 11 | where T: IBlob, U: RangeBounds< u64 > 12 | { 13 | let start: Number = match range.start_bound() { 14 | Bound::Included(&n) => n, 15 | Bound::Excluded(&n) => n + 1, 16 | Bound::Unbounded => 0 17 | }.try_into().unwrap(); 18 | 19 | let end: OptionalArg< Number > = match range.end_bound() { 20 | Bound::Included(&n) => Some(n + 1), 21 | Bound::Excluded(&n) => Some(n), 22 | Bound::Unbounded => None 23 | }.try_into().unwrap(); 24 | 25 | let content_type: OptionalArg< &str > = content_type.into(); 26 | let reference = blob.as_ref(); 27 | js! ( 28 | return @{reference}.slice(@{start}, @{end}, @{content_type}); 29 | ).try_into().unwrap() 30 | } 31 | 32 | /// A blob object represents a file-like object of immutable, raw data. 33 | /// Blobs represent data that isn't necessarily in a JavaScript-native format. 34 | /// 35 | /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/Blob) 36 | // https://w3c.github.io/FileAPI/#dfn-Blob 37 | pub trait IBlob: ReferenceType { 38 | /// The size, in bytes, of the data contained in the Blob object. 39 | /// 40 | /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/Blob/size) 41 | // https://w3c.github.io/FileAPI/#ref-for-dfn-size%E2%91%A0 42 | fn len( &self ) -> u64 { 43 | let reference = self.as_ref(); 44 | let length: u64 = js!( return @{reference}.size; ).try_into().unwrap(); 45 | length 46 | } 47 | 48 | /// A string indicating the MIME type of the data contained in the `Blob`. 49 | /// 50 | /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/Blob/type) 51 | // https://w3c.github.io/FileAPI/#ref-for-dfn-type%E2%91%A0 52 | fn mime( &self ) -> Option< String > { 53 | let reference = self.as_ref(); 54 | let mime: String = js!( return @{reference}.type; ).try_into().unwrap(); 55 | if mime.is_empty() { 56 | None 57 | } else { 58 | Some( mime ) 59 | } 60 | } 61 | 62 | /// Create a new `Blob` object containing the data in the specified range of bytes of the 63 | /// source `Blob`. 64 | /// 65 | /// See also [slice_with_content_type](IBlob::slice_with_content_type). 66 | /// 67 | /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/Blob/slice) 68 | fn slice< T >( &self, range: T ) -> Blob 69 | where T: RangeBounds 70 | { 71 | slice_blob(self, range, None) 72 | } 73 | 74 | /// [slice](IBlob::slice) `Blob` with the provided `content_type`. 75 | fn slice_with_content_type< T >( &self, range: T, content_type: &str ) -> Blob 76 | where T: RangeBounds 77 | { 78 | slice_blob(self, range, Some(content_type)) 79 | } 80 | } 81 | 82 | /// A reference to a JavaScript object which implements the [IBlob](trait.IBlob.html) 83 | /// interface. 84 | /// 85 | /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/Blob) 86 | // https://w3c.github.io/FileAPI/#dfn-Blob 87 | #[derive(Clone, Debug, PartialEq, Eq, ReferenceType)] 88 | #[reference(instance_of = "Blob")] 89 | pub struct Blob( Reference ); 90 | 91 | impl IBlob for Blob {} 92 | 93 | impl Blob { 94 | /// Creates a new `Blob`. 95 | /// 96 | /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/Blob/Blob) 97 | // https://w3c.github.io/FileAPI/#constructorBlob 98 | pub fn new() -> Self { 99 | js! ( 100 | return new Blob(); 101 | ).try_into().unwrap() 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/webapi/child_node.rs: -------------------------------------------------------------------------------- 1 | use webcore::reference_type::ReferenceType; 2 | 3 | /// The `ChildNode` interface contains methods that are particular to `Node` 4 | /// objects that can have a parent. 5 | /// 6 | /// You most likely don't want to `use` this directly; instead 7 | /// you should `use stdweb::traits::*;`. 8 | /// 9 | /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/ChildNode) 10 | // https://dom.spec.whatwg.org/#interface-childnode 11 | pub trait IChildNode: ReferenceType { 12 | /// The `ChildNode.remove()` method removes the object from the tree it belongs to. 13 | /// 14 | /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/ChildNode/remove) 15 | // https://dom.spec.whatwg.org/#ref-for-dom-childnode-remove 16 | fn remove( &self ) { 17 | js! { @(no_return) 18 | @{self.as_ref()}.remove(); 19 | }; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/webapi/console.rs: -------------------------------------------------------------------------------- 1 | #[doc(hidden)] 2 | #[macro_export] 3 | macro_rules! __internal_console_unsafe { 4 | ( $name:ident ) => {{ 5 | $crate::js! { @(no_return) 6 | console.$name(); 7 | } 8 | () 9 | }}; 10 | 11 | ( $name:ident, $( $args:expr ),* ) => {{ 12 | $crate::js! { @(no_return) 13 | console.$name( $( @{$args} ),* ); 14 | } 15 | () 16 | }}; 17 | } 18 | 19 | 20 | /// Calls methods on the JavaScript `console` object. 21 | /// 22 | /// This should **only** be used for debugging purposes, its behavior is 23 | /// **not** standardized: it **will** vary with different browsers 24 | /// and Node.js. 25 | /// 26 | /// The first argument is the name of the `console` method. 27 | /// 28 | /// The remaining arguments can be anything which can be sent to JavaScript, 29 | /// and they do not need to be the same type. 30 | /// 31 | /// If you want to print things to the console in a standardized way, use 32 | /// [`println!`](https://doc.rust-lang.org/std/macro.println.html) instead. 33 | /// 34 | /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/console) 35 | /// 36 | /// # Examples 37 | /// 38 | /// ## log 39 | /// 40 | /// Print a newline: 41 | /// 42 | /// ```rust 43 | /// console!(log, ""); 44 | /// ``` 45 | /// 46 | /// Print one value: 47 | /// 48 | /// ```rust 49 | /// console!(log, "Hello world!"); 50 | /// ``` 51 | /// 52 | /// Print more than one value: 53 | /// 54 | /// ```rust 55 | /// console!(log, 1, "test", vec![2, 3]); 56 | /// ``` 57 | /// 58 | /// Use [string substitution](https://developer.mozilla.org/en-US/docs/Web/API/console#Using_string_substitutions) to control how the values are printed: 59 | /// 60 | /// ```rust 61 | /// console!(log, "foo: %s bar: %s", vec![1, 2], vec![3, 4]); 62 | /// ``` 63 | /// 64 | /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/Console/log) 65 | /// 66 | /// ## error 67 | /// 68 | /// This is exactly the same as `log`, except it prints an error message rather than a normal message. 69 | /// 70 | /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/Console/error) 71 | #[macro_export] 72 | macro_rules! console { 73 | ( log, $( $args:expr ),+ ) => { $crate::__internal_console_unsafe!( log, $( $args ),+ ) }; 74 | ( error, $( $args:expr ),+ ) => { $crate::__internal_console_unsafe!( error, $( $args ),+ ) }; 75 | } 76 | -------------------------------------------------------------------------------- /src/webapi/cross_origin_setting.rs: -------------------------------------------------------------------------------- 1 | 2 | /// Represents CORS (Cross Origin Resource Sharing) setting for an HTML element. 3 | /// 4 | /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_settings_attributes) 5 | // https://html.spec.whatwg.org/#cors-settings-attribute 6 | #[derive(Clone, Copy, Debug, Eq, PartialEq)] 7 | pub enum CrossOriginSetting { 8 | /// CORS is not used for this element. 9 | None, 10 | 11 | /// CORS requests for this element will not have the credentials flag set. 12 | Anonymous, 13 | 14 | /// CORS requests for this element will have the credentials flag set; 15 | /// this means the request will provide credentials. 16 | UseCredentials, 17 | } 18 | -------------------------------------------------------------------------------- /src/webapi/document_fragment.rs: -------------------------------------------------------------------------------- 1 | use webcore::value::Reference; 2 | use webapi::event_target::{IEventTarget, EventTarget}; 3 | use webapi::node::{INode, Node}; 4 | use webapi::parent_node::IParentNode; 5 | 6 | /// A reference to a JavaScript object DocumentFragment. 7 | /// 8 | /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/DocumentFragment) 9 | // https://dom.spec.whatwg.org/#documentfragment 10 | #[derive(Clone, Debug, PartialEq, Eq, ReferenceType)] 11 | #[reference(instance_of = "DocumentFragment")] 12 | #[reference(subclass_of(EventTarget, Node))] 13 | pub struct DocumentFragment( Reference ); 14 | 15 | impl IEventTarget for DocumentFragment {} 16 | impl INode for DocumentFragment {} 17 | impl IParentNode for DocumentFragment {} 18 | -------------------------------------------------------------------------------- /src/webapi/error.rs: -------------------------------------------------------------------------------- 1 | use webcore::value::Reference; 2 | use webcore::try_from::TryInto; 3 | use webcore::reference_type::ReferenceType; 4 | 5 | /// Represents the JavaScript `Error` interface. An `Error` is thrown whenever a run-time error 6 | /// occurs. 7 | /// 8 | /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error) 9 | // https://www.ecma-international.org/ecma-262/6.0/#sec-error-objects 10 | pub trait IError: ReferenceType { 11 | /// Returns a human-readable description of the error. 12 | /// 13 | /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/message) 14 | // https://www.ecma-international.org/ecma-262/6.0/#sec-error.prototype.message 15 | #[inline] 16 | fn message( &self ) -> String { 17 | js!( 18 | return @{self.as_ref()}.message; 19 | ).try_into().unwrap() 20 | } 21 | 22 | /// Returns a name specifiying the type of error. 23 | /// 24 | /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/name) 25 | // https://www.ecma-international.org/ecma-262/6.0/#sec-error.prototype.name 26 | #[inline] 27 | fn name( &self ) -> String { 28 | js!( 29 | return @{self.as_ref()}.name; 30 | ).try_into().unwrap() 31 | } 32 | } 33 | 34 | /// A reference to a JavaScript `Error` object. An `Error` is thrown whenever a run-time error 35 | /// occurs. 36 | /// 37 | /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error) 38 | // https://www.ecma-international.org/ecma-262/6.0/#sec-error-objects 39 | #[derive(Clone, Debug, PartialEq, Eq, ReferenceType)] 40 | #[reference(instance_of = "Error")] 41 | pub struct Error( Reference ); 42 | 43 | impl Error { 44 | /// Creates a new `Error` with the specified `description`. 45 | /// 46 | /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error) 47 | #[inline] 48 | pub fn new( description: &str ) -> Self { 49 | js!( return new Error( @{description} ); ).try_into().unwrap() 50 | } 51 | } 52 | 53 | impl IError for Error {} 54 | 55 | error_boilerplate! { Error } 56 | 57 | /// Used to indicate an unsuccessful operation when none of the other NativeError objects are an appropriate indication of the failure cause. 58 | // https://tc39.github.io/ecma262/#sec-native-error-types-used-in-this-standard-typeerror 59 | #[derive(Clone, Debug, ReferenceType)] 60 | #[reference(subclass_of(Error))] 61 | #[reference(instance_of = "TypeError")] 62 | pub struct TypeError( Reference ); 63 | 64 | impl IError for TypeError {} 65 | 66 | error_boilerplate! { TypeError } 67 | 68 | #[cfg(test)] 69 | mod test { 70 | use super::*; 71 | 72 | #[test] 73 | fn test_error() { 74 | use std::fmt::Write; 75 | 76 | let error: Error = js!( 77 | return new Error("foo"); 78 | ).try_into().unwrap(); 79 | 80 | assert_eq!(error.name(), "Error"); 81 | assert_eq!(error.message(), "foo"); 82 | 83 | let mut text = String::new(); 84 | write!(&mut text, "{}", error).unwrap(); 85 | assert_eq!(&text, "Error: foo"); 86 | assert_eq!(std::error::Error::description(&error), "Error"); 87 | } 88 | 89 | #[test] 90 | fn test_type_error() { 91 | let _: TypeError = js!( return new TypeError( "Big bad wolf" ); ).try_into().unwrap(); 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/webapi/event_target.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | 3 | use webcore::value::Reference; 4 | use webcore::try_from::TryInto; 5 | use webcore::reference_type::ReferenceType; 6 | use webcore::mutfn::Mut; 7 | use webapi::event::{ConcreteEvent, IEvent}; 8 | use private::TODO; 9 | 10 | /// A handle to a particular event listener. 11 | pub struct EventListenerHandle { 12 | event_type: &'static str, 13 | reference: Reference, 14 | listener_reference: Reference 15 | } 16 | 17 | impl fmt::Debug for EventListenerHandle { 18 | fn fmt( &self, formatter: &mut fmt::Formatter ) -> fmt::Result { 19 | write!( formatter, "EventListenerHandle {{ event_type: {}, reference: {:?} }}", self.event_type, self.reference ) 20 | } 21 | } 22 | 23 | impl EventListenerHandle { 24 | /// Removes the listener from the [IEventTarget](trait.IEventTarget.html) on 25 | /// which it was previously registered. 26 | /// 27 | /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/removeEventListener) 28 | // https://dom.spec.whatwg.org/#ref-for-dom-eventtarget-removeeventlistener%E2%91%A0 29 | pub fn remove( self ) { 30 | js! { @(no_return) 31 | var listener = @{&self.listener_reference}; 32 | @{&self.reference}.removeEventListener( @{self.event_type}, listener ); 33 | listener.drop(); 34 | } 35 | } 36 | } 37 | 38 | /// `IEventTarget` is an interface implemented by objects that 39 | /// can receive events and may have listeners for them. 40 | /// 41 | /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget) 42 | // https://dom.spec.whatwg.org/#eventtarget 43 | pub trait IEventTarget: ReferenceType { 44 | /// Adds given event handler to the list of event listeners for 45 | /// the specified `EventTarget` on which it's called. 46 | /// 47 | /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener) 48 | // https://dom.spec.whatwg.org/#ref-for-dom-eventtarget-addeventlistener%E2%91%A0 49 | fn add_event_listener< T, F >( &self, listener: F ) -> EventListenerHandle 50 | where T: ConcreteEvent, F: FnMut( T ) + 'static 51 | { 52 | let reference = self.as_ref(); 53 | 54 | let listener_reference = js! { 55 | var listener = @{Mut(listener)}; 56 | @{reference}.addEventListener( @{T::EVENT_TYPE}, listener ); 57 | return listener; 58 | }.try_into().unwrap(); 59 | 60 | EventListenerHandle { 61 | event_type: T::EVENT_TYPE, 62 | reference: reference.clone(), 63 | listener_reference: listener_reference 64 | } 65 | } 66 | 67 | /// Dispatches an `Event` at this `EventTarget`, invoking the affected event listeners in the 68 | /// appropriate order. 69 | /// 70 | /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/dispatchEvent) 71 | fn dispatch_event< T: IEvent >( &self, event: &T ) -> Result< bool, TODO > { 72 | Ok( js! ( 73 | return @{self.as_ref()}.dispatchEvent( @{event.as_ref()} ); 74 | ).try_into().unwrap() ) 75 | } 76 | } 77 | 78 | /// A reference to a JavaScript object which implements the [IEventTarget](trait.IEventTarget.html) 79 | /// interface. 80 | /// 81 | /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget) 82 | // https://dom.spec.whatwg.org/#eventtarget 83 | #[derive(Clone, Debug, PartialEq, Eq, ReferenceType)] 84 | #[reference(instance_of = "EventTarget")] 85 | pub struct EventTarget( Reference ); 86 | 87 | impl IEventTarget for EventTarget {} 88 | -------------------------------------------------------------------------------- /src/webapi/events/focus.rs: -------------------------------------------------------------------------------- 1 | use webcore::value::Reference; 2 | use webcore::try_from::TryInto; 3 | use webapi::event_target::EventTarget; 4 | use webapi::event::{IEvent, Event}; 5 | 6 | /// The `IFocusEvent` interface represents focus-related 7 | /// events. 8 | /// 9 | /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/FocusEvent) 10 | // https://w3c.github.io/uievents/#focusevent 11 | pub trait IFocusEvent: IEvent { 12 | /// Returns the secondary target of this event, if any. 13 | /// 14 | /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/FocusEvent/relatedTarget) 15 | // https://w3c.github.io/uievents/#dom-focusevent-relatedtarget 16 | #[inline] 17 | fn related_target( &self ) -> Option< EventTarget > { 18 | js!( 19 | return @{self.as_ref()}.relatedTarget; 20 | ).try_into().ok() 21 | } 22 | } 23 | 24 | /// A reference to a JavaScript object which implements the [IFocusEvent](trait.IFocusEvent.html) 25 | /// interface. 26 | /// 27 | /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/FocusEvent) 28 | // https://w3c.github.io/uievents/#focusevent 29 | #[derive(Clone, Debug, PartialEq, Eq, ReferenceType)] 30 | #[reference(instance_of = "FocusEvent")] 31 | #[reference(subclass_of(Event))] 32 | pub struct FocusRelatedEvent( Reference ); 33 | 34 | impl IEvent for FocusRelatedEvent {} 35 | impl IFocusEvent for FocusRelatedEvent {} 36 | 37 | /// The `FocusEvent` is fired when an element has received focus. The main 38 | /// difference between this event and focusin is that only the latter bubbles. 39 | /// 40 | /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/Events/focus) 41 | // https://w3c.github.io/uievents/#event-type-focus 42 | #[derive(Clone, Debug, PartialEq, Eq, ReferenceType)] 43 | #[reference(instance_of = "FocusEvent")] 44 | #[reference(event = "focus")] 45 | #[reference(subclass_of(Event, FocusRelatedEvent))] 46 | pub struct FocusEvent( Reference ); 47 | 48 | impl IEvent for FocusEvent {} 49 | impl IFocusEvent for FocusEvent {} 50 | 51 | /// The `BlurEvent` is fired when an element has lost focus. The main difference 52 | /// between this event and focusout is that only the latter bubbles. 53 | /// 54 | /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/Events/blur) 55 | // https://w3c.github.io/uievents/#event-type-blur 56 | #[derive(Clone, Debug, PartialEq, Eq, ReferenceType)] 57 | #[reference(instance_of = "FocusEvent")] 58 | #[reference(event = "blur")] 59 | #[reference(subclass_of(Event, FocusRelatedEvent))] 60 | pub struct BlurEvent( Reference ); 61 | 62 | impl IEvent for BlurEvent {} 63 | impl IFocusEvent for BlurEvent {} 64 | 65 | #[cfg(all(test, feature = "web_test"))] 66 | mod tests { 67 | use super::*; 68 | use webapi::event::ConcreteEvent; 69 | 70 | #[test] 71 | fn test_focus_event() { 72 | let event: FocusEvent = js!( 73 | return new FocusEvent( "focus" ); 74 | ).try_into().unwrap(); 75 | assert_eq!( event.event_type(), "focus" ); 76 | assert!( event.related_target().is_none() ); 77 | } 78 | 79 | #[test] 80 | fn test_blur_event() { 81 | let event: BlurEvent = js!( 82 | return new FocusEvent( @{BlurEvent::EVENT_TYPE} ); 83 | ).try_into().unwrap(); 84 | assert_eq!( event.event_type(), BlurEvent::EVENT_TYPE ); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/webapi/events/gamepad.rs: -------------------------------------------------------------------------------- 1 | use webcore::value::Reference; 2 | use webcore::try_from::TryInto; 3 | 4 | use webapi::event::{IEvent, Event}; 5 | use webapi::gamepad::Gamepad; 6 | 7 | /// A GamepadEvent is fired on the window object, when a gamepad is connected or disconnected to the system. 8 | /// 9 | /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/GamepadEvent) 10 | // https://w3c.github.io/gamepad/#gamepadevent-interface 11 | pub trait IGamepadEvent: IEvent { 12 | 13 | /// Returns the gamepad associated with this event. 14 | #[inline] 15 | fn gamepad( &self ) -> Gamepad { 16 | js!( 17 | return @{self.as_ref()}.gamepad; 18 | ).try_into().unwrap() 19 | } 20 | } 21 | 22 | /// A reference to a JavaScript object which implements the [IGamepadEvent](trait.IGamepadEvent.html) 23 | /// interface. 24 | /// 25 | /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/GamepadEvent) 26 | // https://w3c.github.io/gamepad/#gamepadevent-interface 27 | #[derive(Clone, Debug, Eq, PartialEq, ReferenceType)] 28 | #[reference(instance_of = "GamepadEvent")] 29 | #[reference(subclass_of(Event))] 30 | pub struct GamepadEvent( Reference ); 31 | 32 | impl IEvent for GamepadEvent {} 33 | impl IGamepadEvent for GamepadEvent {} 34 | 35 | /// The `GamepadConnected` event is fired on the window object, when the first input is received for a gamepad. 36 | /// 37 | /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/Events/gamepadconnected) 38 | // https://w3c.github.io/gamepad/#event-gamepadconnected 39 | #[derive(Clone, Debug, Eq, PartialEq, ReferenceType)] 40 | #[reference(instance_of = "GamepadEvent")] 41 | #[reference(event = "gamepadconnected")] 42 | #[reference(subclass_of(Event, GamepadEvent))] 43 | pub struct GamepadConnectedEvent( Reference ); 44 | 45 | impl IEvent for GamepadConnectedEvent {} 46 | impl IGamepadEvent for GamepadConnectedEvent {} 47 | 48 | /// The `GamepadDisconnected` event is fired on the window object, when a gamepad is disconnected. 49 | /// 50 | /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/Events/gamepaddisconnected) 51 | // https://w3c.github.io/gamepad/#event-gamepaddisconnected 52 | #[derive(Clone, Debug, Eq, PartialEq, ReferenceType)] 53 | #[reference(instance_of = "GamepadEvent")] 54 | #[reference(event = "gamepaddisconnected")] 55 | #[reference(subclass_of(Event, GamepadEvent))] 56 | pub struct GamepadDisconnectedEvent( Reference ); 57 | 58 | impl IEvent for GamepadDisconnectedEvent {} 59 | impl IGamepadEvent for GamepadDisconnectedEvent {} 60 | 61 | #[cfg(all(test, feature = "web_test"))] 62 | mod tests { 63 | use super::*; 64 | 65 | #[test] 66 | fn test_gamepad_connected_event() { 67 | 68 | let event: GamepadConnectedEvent = js!( 69 | return new GamepadEvent("gamepadconnected"); 70 | ).try_into().unwrap(); 71 | 72 | assert_eq!(event.event_type(), "gamepadconnected"); 73 | } 74 | 75 | #[test] 76 | fn test_gamepad_disconnected_event() { 77 | 78 | let event: GamepadDisconnectedEvent = js!( 79 | return new GamepadEvent("gamepaddisconnected"); 80 | ).try_into().unwrap(); 81 | 82 | assert_eq!(event.event_type(), "gamepaddisconnected"); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/webapi/events/history.rs: -------------------------------------------------------------------------------- 1 | use webcore::value::{Reference, Value}; 2 | use webcore::try_from::TryInto; 3 | use webapi::event::{IEvent, Event}; 4 | 5 | /// The `HashChangeEvent` is fired when the fragment 6 | /// identifier of the URL has changed (the part of the URL 7 | /// that follows the # symbol, including the # symbol). 8 | /// 9 | /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/Events/hashchange) 10 | // https://html.spec.whatwg.org/#event-hashchange 11 | // https://html.spec.whatwg.org/#hashchangeevent 12 | #[derive(Clone, Debug, PartialEq, Eq, ReferenceType)] 13 | #[reference(instance_of = "HashChangeEvent")] 14 | #[reference(event = "hashchange")] 15 | #[reference(subclass_of(Event))] 16 | pub struct HashChangeEvent( Reference ); 17 | 18 | impl IEvent for HashChangeEvent {} 19 | 20 | impl HashChangeEvent { 21 | /// The previous URL from which the window was navigated. 22 | /// 23 | /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/HashChangeEvent) 24 | // https://html.spec.whatwg.org/#the-hashchangeevent-interface:dom-hashchangeevent-oldurl 25 | #[inline] 26 | pub fn old_url( &self ) -> String { 27 | js!( 28 | return @{self.as_ref()}.oldURL; 29 | ).try_into().unwrap() 30 | } 31 | 32 | /// The new URL to which the window was navigated. 33 | /// 34 | /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/HashChangeEvent) 35 | // https://html.spec.whatwg.org/#the-hashchangeevent-interface:dom-hashchangeevent-newurl 36 | #[inline] 37 | pub fn new_url( &self ) -> String { 38 | js!( 39 | return @{self.as_ref()}.newURL; 40 | ).try_into().unwrap() 41 | } 42 | } 43 | 44 | /// A `PopStateEvent` is dispatched to the window every time the active history entry changes 45 | /// between two history entries for the same document. If the history entry being activated was 46 | /// created by a call to `history.push_state()` or was affected by a call to 47 | /// `history.replace_state()`, the `PopStateEvent`'s state property contains a copy of the history 48 | /// entry's state object. 49 | /// 50 | /// Note that just calling `history.push_state()` or `history.replace_state()` won't trigger a 51 | /// `PopStateEvent`. The `PopStateEvent` is only triggered by doing a browser action such as a 52 | /// clicking on the back button (or calling `history.back()`). And the event is only 53 | /// triggered when the user navigates between two history entries for the same document. 54 | /// 55 | /// Browsers tend to handle the `PopStateEvent` differently on page load. Chrome and Safari always 56 | /// emit a `PopStateEvent` on page load, but Firefox doesn't. 57 | /// 58 | /// [(Javascript docs)](https://developer.mozilla.org/en-US/docs/Web/API/PopStateEvent) 59 | // https://html.spec.whatwg.org/#event-popstate 60 | // https://html.spec.whatwg.org/#popstateevent 61 | #[derive(Clone, Debug, PartialEq, Eq, ReferenceType)] 62 | #[reference(instance_of = "Event")] 63 | #[reference(event = "popstate")] 64 | #[reference(subclass_of(Event))] 65 | pub struct PopStateEvent(Reference); 66 | 67 | impl PopStateEvent { 68 | /// The state object associated to the new history entry, if that entry was created with 69 | /// push_state or affected by replace_state. 70 | /// 71 | /// Example usage: 72 | /// 73 | /// ```rust,ignore 74 | /// let state: Option = event.state().try_into().ok(); 75 | /// ``` 76 | // https://html.spec.whatwg.org/#dom-popstateevent-state 77 | #[inline] 78 | pub fn state(&self) -> Value { 79 | js!(return @{self}.state;) 80 | } 81 | } 82 | 83 | impl IEvent for PopStateEvent {} 84 | 85 | #[cfg(all(test, feature = "web_test"))] 86 | mod tests { 87 | use super::*; 88 | use webapi::event::ConcreteEvent; 89 | 90 | #[test] 91 | fn test_hash_change_event() { 92 | let event: HashChangeEvent = js!( 93 | return new HashChangeEvent( 94 | @{HashChangeEvent::EVENT_TYPE}, 95 | { 96 | oldURL: "http://test.com#foo", 97 | newURL: "http://test.com#bar" 98 | } 99 | ); 100 | ).try_into().unwrap(); 101 | assert_eq!( event.event_type(), HashChangeEvent::EVENT_TYPE ); 102 | assert_eq!( event.old_url(), "http://test.com#foo" ); 103 | assert_eq!( event.new_url(), "http://test.com#bar" ); 104 | } 105 | 106 | #[test] 107 | fn test_pop_state_event() { 108 | let event: PopStateEvent = js!( 109 | return new PopStateEvent( 110 | @{PopStateEvent::EVENT_TYPE}, 111 | { 112 | state: { 113 | color: "tomato" 114 | } 115 | } 116 | ); 117 | ).try_into().unwrap(); 118 | 119 | assert_eq!(event.event_type(), PopStateEvent::EVENT_TYPE); 120 | 121 | let state_value: Value = event.state(); 122 | let state: std::collections::BTreeMap = state_value 123 | .as_object() 124 | .unwrap() 125 | .into(); 126 | let mut expected = std::collections::BTreeMap::new(); 127 | expected.insert("color".to_string(), "tomato".into()); 128 | 129 | assert_eq!(state, expected); 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /src/webapi/events/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod dom; 2 | pub mod drag; 3 | pub mod focus; 4 | pub mod gamepad; 5 | pub mod history; 6 | pub mod keyboard; 7 | pub mod mouse; 8 | pub mod pointer; 9 | pub mod progress; 10 | pub mod socket; 11 | pub mod slot; 12 | pub mod touch; 13 | -------------------------------------------------------------------------------- /src/webapi/events/slot.rs: -------------------------------------------------------------------------------- 1 | use webcore::value::Reference; 2 | use webapi::event::{IEvent, Event}; 3 | 4 | /// The `slotchange` event is fired on an HTMLSlotElement instance 5 | /// (`` element) when the node(s) contained in that slot change. 6 | /// 7 | /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/Events/slotchange) 8 | // https://dom.spec.whatwg.org/#mutation-observers 9 | #[derive(Clone, Debug, PartialEq, Eq, ReferenceType)] 10 | #[reference(instance_of = "Event")] 11 | #[reference(event = "slotchange")] 12 | #[reference(subclass_of(Event))] 13 | pub struct SlotChangeEvent( Reference ); 14 | 15 | impl IEvent for SlotChangeEvent {} 16 | -------------------------------------------------------------------------------- /src/webapi/file.rs: -------------------------------------------------------------------------------- 1 | use webcore::value::Reference; 2 | use webcore::try_from::TryInto; 3 | use webapi::blob::{IBlob, Blob}; 4 | 5 | /// The File interface provides information about files and allows JavaScript 6 | /// in a web page to access their content. 7 | /// 8 | /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/File) 9 | // https://w3c.github.io/FileAPI/#dfn-file 10 | #[derive(Clone, Debug, PartialEq, Eq, ReferenceType)] 11 | #[reference(instance_of = "File")] 12 | #[reference(subclass_of(Blob))] 13 | pub struct File( pub(crate) Reference ); 14 | 15 | impl IBlob for File {} 16 | 17 | impl File { 18 | /// Returns the name of the file referenced by the `File` object. 19 | /// 20 | /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/File/name) 21 | // https://w3c.github.io/FileAPI/#ref-for-dfn-name%E2%91%A0 22 | pub fn name( &self ) -> String { 23 | js!( return @{self}.name; ).try_into().unwrap() 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/webapi/file_list.rs: -------------------------------------------------------------------------------- 1 | use webcore::value::{Value, Reference}; 2 | use webcore::try_from::TryInto; 3 | use webcore::reference_type::ReferenceType; 4 | use webapi::file::File; 5 | 6 | /// An object of this type is returned by the files property of the HTML `` element; 7 | /// this lets you access the list of files selected with the `` element. 8 | /// It's also used for a list of files dropped into web content when using the drag and drop API. 9 | /// 10 | /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/FileList) 11 | // https://w3c.github.io/FileAPI/#dfn-filelist 12 | #[derive(Clone, Debug, PartialEq, Eq, ReferenceType)] 13 | #[reference(instance_of = "FileList")] 14 | pub struct FileList( Reference ); 15 | 16 | impl FileList { 17 | /// Returns the number of [File](struct.File.html)s contained in this list. 18 | /// 19 | /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/FileList/length) 20 | // https://w3c.github.io/FileAPI/#ref-for-dfn-length 21 | pub fn len( &self ) -> u32 { 22 | js!( return @{self}.length; ).try_into().unwrap() 23 | } 24 | 25 | /// Returns an iterator over the list. 26 | pub fn iter( &self ) -> FileIter { 27 | FileIter { 28 | list: self.clone(), 29 | index: 0 30 | } 31 | } 32 | } 33 | 34 | impl IntoIterator for FileList { 35 | type Item = File; 36 | type IntoIter = FileIter; 37 | 38 | #[inline] 39 | fn into_iter( self ) -> Self::IntoIter { 40 | FileIter { 41 | list: self, 42 | index: 0 43 | } 44 | } 45 | } 46 | 47 | impl< 'a > IntoIterator for &'a FileList { 48 | type Item = File; 49 | type IntoIter = FileIter; 50 | 51 | #[inline] 52 | fn into_iter( self ) -> Self::IntoIter { 53 | FileIter { 54 | list: self.clone(), 55 | index: 0 56 | } 57 | } 58 | } 59 | 60 | #[derive(Debug)] 61 | pub struct FileIter { 62 | list: FileList, 63 | index: i32 64 | } 65 | 66 | impl Iterator for FileIter { 67 | type Item = File; 68 | fn next( &mut self ) -> Option< Self::Item > { 69 | let value = js!( 70 | return @{&self.list}[ @{self.index} ]; 71 | ); 72 | 73 | let file = match value { 74 | Value::Undefined => return None, 75 | Value::Reference( reference ) => unsafe { File::from_reference_unchecked( reference ) }, 76 | _ => unreachable!() 77 | }; 78 | 79 | self.index += 1; 80 | Some( file ) 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/webapi/file_reader.rs: -------------------------------------------------------------------------------- 1 | use webcore::value::{Value, Reference}; 2 | use webcore::try_from::TryInto; 3 | use webapi::blob::IBlob; 4 | use webapi::event_target::{IEventTarget, EventTarget}; 5 | use webapi::array_buffer::ArrayBuffer; 6 | use private::TODO; 7 | 8 | /// The FileReader object lets web applications asynchronously read the contents of files 9 | /// (or raw data buffers) stored on the user's computer, using [File](struct.File.html) 10 | /// or [Blob](struct.Blob.html) objects to specify the file or data to read. 11 | /// 12 | /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/FileReader) 13 | // https://w3c.github.io/FileAPI/#dfn-filereader 14 | #[derive(Clone, Debug, PartialEq, Eq, ReferenceType)] 15 | #[reference(instance_of = "FileReader")] 16 | #[reference(subclass_of(EventTarget))] 17 | pub struct FileReader( Reference ); 18 | 19 | impl IEventTarget for FileReader {} 20 | 21 | /// The [result](struct.FileReader.html#method.result) of a read operation performed with a [FileReader](struct.File.html). 22 | #[derive(Clone, Debug)] 23 | pub enum FileReaderResult { 24 | /// A string; a result of calling [FileReader::read_as_text](struct.FileReader.html#method.read_as_text). 25 | String( String ), 26 | 27 | /// An [ArrayBuffer](struct.ArrayBuffer.html); a result of calling [FileReader::read_as_array_buffer](struct.FileReader.html#method.read_as_array_buffer). 28 | ArrayBuffer( ArrayBuffer ) 29 | } 30 | 31 | /// A number indicating the state of the `FileReader`. 32 | /// 33 | /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/FileReader/readyState) 34 | #[derive(Copy, Clone, PartialEq, Eq, Debug)] 35 | pub enum FileReaderReadyState { 36 | /// No data has been loaded yet. 37 | Empty, 38 | /// Data is currently being loaded. 39 | Loading, 40 | /// The entire read request has been completed. 41 | Done 42 | } 43 | 44 | impl FileReader { 45 | /// Returns a newly constructed `FileReader`. 46 | /// 47 | /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/FileReader/FileReader) 48 | // https://w3c.github.io/FileAPI/#dom-filereader-filereader 49 | pub fn new() -> FileReader { 50 | js!( return new FileReader(); ).try_into().unwrap() 51 | } 52 | 53 | /// Starts reading the contents of the specified blob. Once finished 54 | /// the `result` attribute will contain the contents of the file as a text string. 55 | /// 56 | /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/FileReader/readAsText) 57 | // https://w3c.github.io/FileAPI/#ref-for-dfn-readAsText 58 | pub fn read_as_text< T: IBlob >( &self, blob: &T ) -> Result< (), TODO > { 59 | js!( @{self}.readAsText( @{blob.as_ref()} ); ); 60 | Ok(()) 61 | } 62 | 63 | /// Starts reading the contents of the specified blob. Once finished 64 | /// the `result` attribute will contain the contents of the file as an [TypedArray](struct.ArrayBuffer.html). 65 | /// 66 | /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/FileReader/readAsArrayBuffer) 67 | // https://w3c.github.io/FileAPI/#ref-for-dfn-readAsArrayBuffer 68 | pub fn read_as_array_buffer< T: IBlob >( &self, blob: &T ) -> Result< (), TODO > { 69 | js!( @{self}.readAsArrayBuffer( @{blob.as_ref()} ); ); 70 | Ok(()) 71 | } 72 | 73 | /// Aborts the read operation. Upon return, the `ready_state` will be `Done`. 74 | /// 75 | /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/FileReader/abort) 76 | // https://w3c.github.io/FileAPI/#ref-for-dfn-abort%E2%91%A0 77 | pub fn abort( &self ) { 78 | js!( return @{self}.abort(); ); 79 | } 80 | 81 | /// Returns the current state of the reader. 82 | /// 83 | /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/FileReader/readyState) 84 | // https://w3c.github.io/FileAPI/#ref-for-dfn-readyState 85 | pub fn ready_state( &self ) -> FileReaderReadyState { 86 | let state: i32 = js!( return @{self}.readyState; ).try_into().unwrap(); 87 | match state { 88 | 0 => FileReaderReadyState::Empty, 89 | 1 => FileReaderReadyState::Loading, 90 | 2 => FileReaderReadyState::Done, 91 | _ => unreachable!( "Unexpected value of FileReader::readyState: {}", state ) 92 | } 93 | } 94 | 95 | /// The file's contents. This method will only return a value after the read operation 96 | /// is complete, and the format of the data depends on which of the methods was used 97 | /// to initiate the read operation. 98 | /// 99 | /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/FileReader/result) 100 | // https://w3c.github.io/FileAPI/#ref-for-dfn-result 101 | pub fn result( &self ) -> Option< FileReaderResult > { 102 | let result = js!( return @{self}.result; ); 103 | match result { 104 | Value::Undefined | Value::Null => None, 105 | Value::String( text ) => Some( FileReaderResult::String( text ) ), 106 | Value::Reference( reference ) => Some( FileReaderResult::ArrayBuffer( reference.try_into().unwrap() ) ), 107 | _ => unreachable!( "Unexpected result of a FileReader: {:?}", result ) 108 | } 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /src/webapi/global.rs: -------------------------------------------------------------------------------- 1 | use webapi::window::window; 2 | use webapi::window_or_worker::IWindowOrWorker; 3 | 4 | /// An alias for [window.set_timeout](trait.IWindowOrWorker.html#method.set_timeout). 5 | pub fn set_timeout< F: FnOnce() + 'static >( callback: F, timeout: u32 ) { 6 | window().set_timeout( callback, timeout ); 7 | } 8 | 9 | /// An alias for [window.alert](struct.Window.html#method.alert). 10 | pub fn alert( message: &str ) { 11 | window().alert( message ); 12 | } 13 | 14 | /// An alias for [window.confirm](struct.Window.html#method.confirm). 15 | pub fn confirm( message: &str ) -> bool { 16 | return window().confirm( message ); 17 | } 18 | -------------------------------------------------------------------------------- /src/webapi/history.rs: -------------------------------------------------------------------------------- 1 | use webcore::value::Reference; 2 | use webcore::try_from::TryInto; 3 | use webcore::serialization::JsSerialize; 4 | use private::TODO; 5 | 6 | /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/History) 7 | // https://html.spec.whatwg.org/#history-3 8 | #[derive(Clone, Debug, PartialEq, Eq, ReferenceType)] 9 | #[reference(instance_of = "History")] 10 | pub struct History(Reference); 11 | 12 | impl History { 13 | /// Adds a new entry to history. 14 | /// 15 | /// pushState() takes three parameters: a state object, a title (which is currently ignored), 16 | /// and (optionally) a URL. Let's examine each of these three parameters in more detail: 17 | /// 18 | /// - state object — The state object is a JavaScript object which is associated with the new 19 | /// history entry created by pushState(). Whenever the user navigates to the new state, a 20 | /// popstate event is fired, and the state property of the event contains a copy of the history 21 | /// entry's state object. 22 | /// 23 | /// - title — Firefox currently ignores this parameter, although it may use it in the future. 24 | /// Passing the empty string here should be safe against future changes to the method. 25 | /// Alternatively, you could pass a short title for the state to which you're moving. 26 | /// 27 | /// - URL — The new history entry's URL is given by this parameter. Note that the browser won't 28 | /// attempt to load this URL after a call to pushState(), but it might attempt to load the URL 29 | /// later, for instance after the user restarts the browser. The new URL does not need to be 30 | /// absolute; if it's relative, it's resolved relative to the current URL. The new URL must be 31 | /// of the same origin as the current URL; otherwise, pushState() will throw an exception. 32 | /// This parameter is optional; if it isn't specified, it's set to the document's current URL. 33 | /// 34 | /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/History_API#The_pushState%28%29_method) 35 | // https://html.spec.whatwg.org/#the-history-interface:dom-history-pushstate 36 | pub fn push_state(&self, state: T, title: &str, url: Option<&str>) { 37 | js!{ @(no_return) 38 | @{self}.pushState(@{state}, @{title}, @{url}); 39 | }; 40 | } 41 | 42 | /// Operates exactly like history.push_state() except that replace_state() modifies the current 43 | /// history entry instead of creating a new one. Note that this doesn't prevent the creation of 44 | /// a new entry in the global browser history. 45 | /// 46 | /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/History_API#The_replaceState%28%29_method) 47 | // https://html.spec.whatwg.org/#the-history-interface:dom-history-replacestate 48 | pub fn replace_state(&self, state: T, title: &str, url: Option<&str>) -> Result< (), TODO > { 49 | js!{ @(no_return) 50 | @{self}.replaceState(@{state}, @{title}, @{url}); 51 | }; 52 | Ok(()) 53 | } 54 | 55 | /// You can use the go() method to load a specific page from session history, identified by its 56 | /// relative position to the current page (with the current page being, of course, relative 57 | /// index 0). 58 | /// 59 | /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/History_API#Traveling_through_history) 60 | // https://html.spec.whatwg.org/#the-history-interface:dom-history-go 61 | pub fn go(&self, offset: i32) -> Result< (), TODO > { 62 | js! { @(no_return) 63 | @{self}.go(@{offset}); 64 | }; 65 | Ok(()) 66 | } 67 | 68 | /// Move one step backward through history. 69 | /// 70 | /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/History_API#Traveling_through_history) 71 | // https://html.spec.whatwg.org/#the-history-interface:dom-history-back 72 | pub fn back(&self) -> Result< (), TODO > { 73 | js! { @(no_return) 74 | @{self}.back(); 75 | }; 76 | Ok(()) 77 | } 78 | 79 | /// Move one step forward through history. 80 | /// 81 | /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/History_API#Traveling_through_history) 82 | // https://html.spec.whatwg.org/#the-history-interface:dom-history-forward 83 | pub fn forward(&self) -> Result< (), TODO > { 84 | js! { @(no_return) 85 | @{self}.forward(); 86 | }; 87 | Ok(()) 88 | } 89 | 90 | /// Returns the current number of history entries. 91 | /// 92 | /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/History) 93 | // https://html.spec.whatwg.org/#the-history-interface:dom-history-length 94 | pub fn len(&self) -> u32 { 95 | js!( 96 | return @{self}.length; 97 | ).try_into().unwrap() 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /src/webapi/html_collection.rs: -------------------------------------------------------------------------------- 1 | use webcore::value::Reference; 2 | use webcore::try_from::TryInto; 3 | use webapi::element::Element; 4 | 5 | /// The `HtmlCollection` interface represents a generic collection 6 | /// (array-like object similar to arguments) of elements (in document order) 7 | /// and offers methods and properties for selecting from the list. 8 | /// 9 | /// An `HtmlCollection` in the HTML DOM is live; it is automatically 10 | /// updated when the underlying document is changed. 11 | /// 12 | /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/HTMLCollection) 13 | // https://dom.spec.whatwg.org/#interface-htmlcollection 14 | #[derive(Clone, Debug, PartialEq, Eq, ReferenceType)] 15 | #[reference(instance_of = "HTMLCollection")] 16 | pub struct HtmlCollection( Reference ); 17 | 18 | impl HtmlCollection { 19 | /// Returns the number of elements in the collection. 20 | /// 21 | /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/HTMLCollection) 22 | // https://dom.spec.whatwg.org/#ref-for-dom-htmlcollection-length 23 | pub fn len( &self ) -> u32 { 24 | js!( return @{self}.length; ).try_into().unwrap() 25 | } 26 | 27 | /// Returns an element from an `HtmlCollection` by index. 28 | /// 29 | /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/HTMLCollection/item) 30 | // https://dom.spec.whatwg.org/#ref-for-dom-htmlcollection-item 31 | pub fn item( &self, index: u32 ) -> Option< Element > { 32 | js!( 33 | return @{self}.item(@{index}); 34 | ).try_into().unwrap() 35 | } 36 | 37 | /// Returns an iterator over the collection. 38 | pub fn iter( &self ) -> ElementIter { 39 | ElementIter { 40 | list: self.clone(), 41 | index: 0 42 | } 43 | } 44 | } 45 | 46 | 47 | impl IntoIterator for HtmlCollection { 48 | type Item = Element; 49 | type IntoIter = ElementIter; 50 | 51 | #[inline] 52 | fn into_iter( self ) -> Self::IntoIter { 53 | ElementIter { 54 | list: self, 55 | index: 0 56 | } 57 | } 58 | } 59 | 60 | impl< 'a > IntoIterator for &'a HtmlCollection { 61 | type Item = Element; 62 | type IntoIter = ElementIter; 63 | 64 | #[inline] 65 | fn into_iter( self ) -> Self::IntoIter { 66 | ElementIter { 67 | list: self.clone(), 68 | index: 0 69 | } 70 | } 71 | } 72 | 73 | #[derive(Debug)] 74 | pub struct ElementIter { 75 | list: HtmlCollection, 76 | index: u32 77 | } 78 | 79 | impl Iterator for ElementIter { 80 | type Item = Element; 81 | fn next( &mut self ) -> Option< Self::Item > { 82 | let item = self.list.item(self.index); 83 | self.index += 1; 84 | item 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/webapi/html_elements/canvas.rs: -------------------------------------------------------------------------------- 1 | use webcore::value::Reference; 2 | use webcore::try_from::TryInto; 3 | use webcore::once::Once; 4 | use webapi::event_target::{IEventTarget, EventTarget}; 5 | use webapi::node::{INode, Node}; 6 | use webapi::element::{IElement, Element}; 7 | use webapi::html_element::{IHtmlElement, HtmlElement}; 8 | use webapi::blob::Blob; 9 | use webapi::rendering_context::RenderingContext; 10 | use private::TODO; 11 | 12 | /// The HTML `` element provides an empty graphic zone on which specific JavaScript APIs 13 | /// can draw (such as Canvas 2D or WebGL). 14 | /// 15 | /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement) 16 | // https://html.spec.whatwg.org/#htmlcanvaselement 17 | #[derive(Clone, Debug, PartialEq, Eq, ReferenceType)] 18 | #[reference(instance_of = "HTMLCanvasElement")] 19 | #[reference(subclass_of(EventTarget, Node, Element, HtmlElement))] 20 | pub struct CanvasElement( Reference ); 21 | 22 | impl IEventTarget for CanvasElement {} 23 | impl INode for CanvasElement {} 24 | impl IElement for CanvasElement {} 25 | impl IHtmlElement for CanvasElement {} 26 | 27 | impl CanvasElement { 28 | /// Returns a positive integer reflecting the height HTML attribute of the element 29 | /// interpreted in CSS pixels. When the attribute is not specified, or if it is set to an 30 | /// invalid value, like a negative, the default value of 150 is used. 31 | /// 32 | /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/height) 33 | // https://html.spec.whatwg.org/#the-canvas-element:dom-canvas-height 34 | pub fn height( &self ) -> u32 { 35 | js! ( 36 | return @{self}.height; 37 | ).try_into().unwrap() 38 | } 39 | 40 | /// Sets a positive integer reflecting the height HTML attribute of the element 41 | /// interpreted in CSS pixels. When the attribute is not specified, or if it is set to an 42 | /// invalid value, like a negative, the default value of 150 is used. 43 | /// 44 | /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/height) 45 | // https://html.spec.whatwg.org/#the-canvas-element:dom-canvas-height 46 | pub fn set_height( &self, value: u32 ) { 47 | js! { @(no_return) 48 | @{self}.height = @{value}; 49 | } 50 | } 51 | 52 | /// Returns a positive integer reflecting the width HTML attribute of the element 53 | /// interpreted in CSS pixels. When the attribute is not specified, or if it is set to an 54 | /// invalid value, like a negative, the default value of 300 is used. 55 | /// 56 | /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/width) 57 | // https://html.spec.whatwg.org/#the-canvas-element:dom-canvas-width 58 | pub fn width( &self ) -> u32 { 59 | js! ( 60 | return @{self}.width; 61 | ).try_into().unwrap() 62 | } 63 | 64 | /// Sets a positive integer reflecting the width HTML attribute of the element 65 | /// interpreted in CSS pixels. When the attribute is not specified, or if it is set to an 66 | /// invalid value, like a negative, the default value of 300 is used. 67 | /// 68 | /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/width) 69 | // https://html.spec.whatwg.org/#the-canvas-element:dom-canvas-width 70 | pub fn set_width( &self, value: u32 ) { 71 | js! { @(no_return) 72 | @{self}.width = @{value}; 73 | } 74 | } 75 | 76 | /// Returns a drawing context on the canvas, or None if the context identifier is not supported. 77 | /// 78 | /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/getContext) 79 | // https://html.spec.whatwg.org/#the-canvas-element:dom-canvas-getcontext 80 | pub fn get_context( &self ) -> Result { 81 | T::from_canvas(self) 82 | } 83 | 84 | /// Returns a data URI containing a representation of the image in the format specified by the 85 | /// type parameter (defaults to PNG). The returned image is in a resolution of 96 dpi. 86 | /// 87 | /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toDataUrl) 88 | // https://html.spec.whatwg.org/#the-canvas-element:dom-canvas-todataurl 89 | pub fn to_data_url( &self, mime_type: Option<&str>, quality: Option ) -> Result< String, TODO > { 90 | Ok( js! ( 91 | return @{self}.toDataURL(@{mime_type}, @{quality}); 92 | ).try_into().unwrap() ) 93 | } 94 | 95 | /// Creates a Blob object representing the image contained in the canvas; this file may be 96 | /// cached on the disk or stored in memory at the discretion of the user agent. 97 | /// 98 | /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toBlob) 99 | // https://html.spec.whatwg.org/#the-canvas-element:dom-canvas-toblob 100 | pub fn to_blob( &self, f: F, mime_type: Option<&str>, quality: Option ) -> Result< (), TODO > { 101 | js! { @(no_return) 102 | @{self}.toBlob(@{Once(f)}, @{mime_type}, @{quality}); 103 | } 104 | 105 | Ok(()) 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /src/webapi/html_elements/input.rs: -------------------------------------------------------------------------------- 1 | use webcore::value::Reference; 2 | use webcore::try_from::TryInto; 3 | use webapi::dom_exception::InvalidStateError; 4 | use webapi::event_target::{IEventTarget, EventTarget}; 5 | use webapi::node::{INode, Node}; 6 | use webapi::element::{IElement, Element}; 7 | use webapi::html_element::{IHtmlElement, HtmlElement}; 8 | 9 | /// The HTML input element is used to create interactive controls 10 | /// for web-based forms in order to accept data from the user. 11 | /// 12 | /// [(JavaScript docs)](https://developer.mozilla.org/en/docs/Web/HTML/Element/input) 13 | // https://html.spec.whatwg.org/#htmlinputelement 14 | #[derive(Clone, Debug, PartialEq, Eq, ReferenceType)] 15 | #[reference(instance_of = "HTMLInputElement")] 16 | #[reference(subclass_of(EventTarget, Node, Element, HtmlElement))] 17 | pub struct InputElement( Reference ); 18 | 19 | impl IEventTarget for InputElement {} 20 | impl INode for InputElement {} 21 | impl IElement for InputElement {} 22 | impl IHtmlElement for InputElement {} 23 | 24 | impl InputElement { 25 | /// The value of the control. This attribute is optional except when the input is a radio button or a checkbox. 26 | /// 27 | // https://html.spec.whatwg.org/#the-input-element:dom-input-value 28 | #[inline] 29 | pub fn raw_value( &self ) -> String { 30 | js! ( 31 | return @{self}.value; 32 | ).try_into().unwrap() 33 | } 34 | 35 | /// Sets the value of the control. 36 | /// 37 | // https://html.spec.whatwg.org/#dom-input-value 38 | #[inline] 39 | pub fn set_raw_value( &self, value: &str ) { 40 | js! { @(no_return) 41 | @{self}.value = @{value}; 42 | } 43 | } 44 | 45 | /// The offset to the start of the selection. 46 | /// This attribute only applies when the input is a text, search, url, telephone or password. 47 | /// 48 | // https://html.spec.whatwg.org/#the-input-element:dom-textarea/input-selectionstart 49 | #[inline] 50 | pub fn selection_start( &self ) -> Option { 51 | js! ( 52 | return @{self}.selectionStart; 53 | ).try_into().ok() 54 | } 55 | 56 | /// Sets the offset to the start of the selection. 57 | /// This attribute only applies when the input is a text, search, url, telephone or password. 58 | /// 59 | // https://html.spec.whatwg.org/#the-input-element:dom-textarea/input-selectionstart 60 | #[inline] 61 | pub fn set_selection_start( &self, value: u32 ) -> Result<(), InvalidStateError> { 62 | js_try! ( @(no_return) 63 | @{self}.selectionStart = @{value}; 64 | ).unwrap() 65 | } 66 | 67 | /// The offset to the end of the selection. 68 | /// This attribute only applies when the input is a text, search, url, telephone or password. 69 | /// 70 | // https://html.spec.whatwg.org/#the-input-element:dom-textarea/input-selectionstart 71 | #[inline] 72 | pub fn selection_end( &self ) -> Option { 73 | js! ( 74 | return @{self}.selectionEnd; 75 | ).try_into().ok() 76 | } 77 | 78 | /// Sets the offset to the end of the selection. 79 | /// This attribute only applies when the input is a text, search, url, telephone or password. 80 | /// 81 | // https://html.spec.whatwg.org/#the-input-element:dom-textarea/input-selectionstart 82 | #[inline] 83 | pub fn set_selection_end( &self, value: u32 ) -> Result<(), InvalidStateError> { 84 | js_try! ( @(no_return) 85 | @{self}.selectionEnd = @{value}; 86 | ).unwrap() 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/webapi/html_elements/mod.rs: -------------------------------------------------------------------------------- 1 | mod canvas; 2 | mod image; 3 | mod input; 4 | mod textarea; 5 | mod select; 6 | mod option; 7 | mod template; 8 | mod slot; 9 | 10 | pub use self::canvas::CanvasElement; 11 | pub use self::image::ImageElement; 12 | pub use self::input::InputElement; 13 | pub use self::textarea::TextAreaElement; 14 | pub use self::select::SelectElement; 15 | pub use self::option::OptionElement; 16 | pub use self::template::TemplateElement; 17 | pub use self::slot::{SlotElement, SlotContentKind}; 18 | 19 | pub use self::select::UnknownValueError; 20 | -------------------------------------------------------------------------------- /src/webapi/html_elements/option.rs: -------------------------------------------------------------------------------- 1 | use webapi::element::{Element, IElement}; 2 | use webapi::event_target::{EventTarget, IEventTarget}; 3 | use webapi::html_element::{HtmlElement, IHtmlElement}; 4 | use webapi::node::{INode, Node}; 5 | use webcore::try_from::TryInto; 6 | use webcore::value::Reference; 7 | 8 | /// The HTML `