Referring to a child view from the parent view on click or tap event in TI - view

In appcelerator TI code - I have a month scrollable view to which I have added week views and to which I have added days as views.
The expectation on click event of a day I should be able to retrieve properties of the date. However on singletap event I am getting reference to the week view and not able to get the child view "days view. how can I get a reference to the days view on single tap to click event?
Code -Widget.js
var args = arguments[0] || {};
var Moment = require('alloy/moment');
var ROWS = 6;
var COLUMNS = 7;
_.defaults(args, {
// Data
current_date: Moment(),
active_dates: [],
min_date: Moment().subtract(6, 'months'),
max_date: Moment().add(6, 'months'),
// Style
backgroundColor: 'transparent',
dateBackgroundColor: 'transparent',
todayBackgroundColor: '#af80',
dateTextColor: '#fff',
todayTextColor: '#000',
activePinColor: '#f39911',
inactivePinColor: 'transparent',
selectedBackgroundColor: '#60f39911',
fontFamily: '',
// Behaviour
allowInactiveSelection: false,
fillMonth: false,
enablePastDays: false
});
var active_dates = args.active_dates ? getMomentDates(args.active_dates) : [];
var current_page = 0;
/////////////
// Methods //
/////////////
function refreshArrows() {
$.leftBtn.opacity = current_page <= 0 ? 0.4 : 1;
$.rightBtn.opacity = current_page >= $.monthScroll.views.length - 1 ? 0.4 : 1;
}
function getDayLabels() {
var days = Moment.weekdaysMin();
days.push(days.shift()); // Moment week has Sunday at index 0
_.each(days, function(day, i) {
var width = Math.floor($.calendar.rect.width / COLUMNS);
var $label = $.UI.create('Label', {
classes: ['dayLabel'],
width: width,
text: day.charAt(0),
left: i * width,
font: {
fontFamily: args.fontFamily
}
});
$.dayLabels.add($label);
});
}
function getMomentDates(dates) {
return _.map(dates, function(date) {
return Moment(date);
});
}
function isInMomentsList(date, dates) {
return _.find(dates, function(day) {
return date.isSame(day, 'day');
});
}
function getDayContainer(number) {
var $this = $.UI.create('View', {
classes: ['day'],
width: Math.floor($.monthScroll.rect.width / COLUMNS),
height: Math.floor($.monthScroll.rect.height / ROWS),
backgroundColor: args.dateBackgroundColor,
opacity: 1,
date: null,
active: null,
});
$this.add($.UI.create('Label', {
classes: ['dayNumber'],
color: '#fff',
text: number,
font: {
fontFamily: args.fontFamily
}
}));
$this.add($.UI.create('View', {
classes: ['dayDot'],
backgroundColor: 'transparent'
}));
return $this;
}
function setItemDate($item, date) {
$item.date = date;
$item.children[0].text = date.date();
}
function setItemActive($item, active) {
$item.active = active;
$item.children[1].backgroundColor = active ? args.activePinColor : args.inactivePinColor;
}
function setItemToday($item, is_today) {
$item.backgroundColor = is_today ? args.todayBackgroundColor : args.dateBackgroundColor;
$item.children[0].color = is_today ? args.todayTextColor : args.dateTextColor;
}
function setItemCurrent($item, current) {
$item.opacity = current ? 1 : 0.5;
}
function getMonthView(month, year) {
var month_rows = [];
var start_date = Moment().month(month).year(year).startOf('month').startOf('week');
var end_date = Moment().month(month).year(year).endOf('month').endOf('week');
// Month skeleton
var $month_view = $.UI.create('View', {
classes: ['month'],
month: month,
year: year,
backgroundColor: args.backgroundColor,
ready: false
});
// Month activity indicator
var $loader = Ti.UI.createActivityIndicator({
style: OS_IOS ? Ti.UI.iPhone.ActivityIndicatorStyle.BIG : Ti.UI.ActivityIndicatorStyle.BIG,
center: {
x: '50%',
y: '50%'
}
});
$month_view.add($loader);
$month_view.__loader = $loader;
$loader.show();
return $month_view;
}
function buildMonth($month_view, dates) {
if (!$month_view || $month_view.ready) return;
var start_date = Moment().month($month_view.month).year($month_view.year).startOf('month').startOf('week');
var end_date = Moment().month($month_view.month).year($month_view.year).endOf('month').endOf('week');
var $days_container = Ti.UI.createView({
height: Ti.UI.FILL,
width: Ti.UI.FILL
});
// Separators
for (var i = 0; i < ROWS; i++) {
$days_container.add($.UI.create('View', {
classes: ['hr'],
top: (i+1) * Math.floor($.monthScroll.rect.height / ROWS)
}));
}
// Add day containers
for (var d = 0; d < ROWS*COLUMNS; d++) {
var curday = Moment(start_date).add(d, 'days');
// If fillMonth is disabled, add only this month's days
if (curday.month() === $month_view.month || args.fillMonth == true) {
var $curview = getDayContainer(curday.date());
var row = Math.floor(d/COLUMNS);
var col = d % COLUMNS;
setItemDate($curview, curday);
setItemActive($curview, isInMomentsList(curday, dates));
setItemCurrent($curview, !curday.isBefore(Moment(), 'day') || (args.enablePastDays == true && (curday.month() === $month_view.month)));
setItemToday($curview, curday.isSame(Moment(), 'day'));
$curview.top = row * ($curview.height);
$curview.left = col * ($curview.width);
$days_container.add($curview);
}
}
$month_view.add($days_container);
$month_view.ready = true;
$month_view.__loader.hide();
}
function buildCalendar() {
$.main.removeEventListener('postlayout', buildCalendar);
// Add top labels
getDayLabels();
// Create the calendar views
var curmonth_index = -1; var i = 0;
for (var m = Moment(args.min_date); m.diff(Moment(args.max_date)) <= 0; m.add(1, 'months')) {
if (m.isSame(Moment(), 'month')) curmonth_index = i;
var monthview = getMonthView(m.month(), m.year());
$.monthScroll.addView(monthview);
i++;
}
$.monthScroll.currentPage = current_page = curmonth_index > 0 ? curmonth_index : 0;
refreshCalendarMonth(current_page);
refreshArrows();
}
function refreshCalendarMonth(m) {
var month_date = Moment().month($.monthScroll.views[m].month).year($.monthScroll.views[m].year);
$.monthName.text = month_date.format('MMMM').toUpperCase();
$.monthYear.text = month_date.format('YYYY');
buildMonth($.monthScroll.views[m], args.active_dates);
if (current_page - 1 > -1) buildMonth($.monthScroll.views[m-1], args.active_dates);
if (current_page + 1 < 12) buildMonth($.monthScroll.views[m+1], args.active_dates);
}
///////////////
// Listeners //
///////////////
$.main.addEventListener('postlayout', buildCalendar);
$.monthScroll.addEventListener('scroll', function(e) {
if (e.currentPage === current_page) return;
current_page = e.currentPage;
refreshArrows();
refreshCalendarMonth(current_page);
});
$.monthScroll.addEventListener('click', function(e) {
if (!e.source.date || (!e.source.active && !args.allowInactiveSelection) || (args.enablePastDays == false && e.source.date.isBefore(Moment(), 'day'))) return;
e.source.animate({ backgroundColor: args.selectedBackgroundColor, duration: 150, autoreverse: true });
$.trigger('selected', {
date: e.source.date,
active: e.source.active
});
});
$.leftBtn.addEventListener('click', function() {
$.monthScroll.movePrevious();
});
$.rightBtn.addEventListener('click', function() {
$.monthScroll.moveNext();
});
//////////
// Init //
//////////
$.monthName.font = {
fontFamily: args.fontFamily,
fontWeight: 'bold'
};
$.monthYear.font = {
fontFamily: args.fontFamily,
fontWeight: 'light'
};
Widget.xml
<Alloy>
<Window backgroundColor="#110ee1" class="container" exitOnClose="true" id="widget" title="DailyRead" top="0">
<View id="main">
<View class="bar" id="header">
<View class="hr" top="0"/>
<View class="ctrlBtn" id="leftBtn">
<ImageView id="leftArrow"/>
</View>
<View class="headerText">
<Label id="monthName"/>
<Label id="monthYear"/>
</View>
<View class="ctrlBtn" id="rightBtn">
<ImageView id="rightArrow"/>
</View>
<View bottom="0" class="hr"/>
</View>
<View class="sp1/2"/>
<View id="calendar">
<View id="dayLabels"/>
<View backgroundColor="#fff" class="hr" height="2"/>
<ScrollableView id="monthScroll"/>
</View>
</View>
</Window>
</Alloy>

As far as I understood the question I would add more information to the day view. E.g. if you create everything through a loop just add month, weeks to the day view as a property. Then set all views but the day view to touchEnabled:false and just add the click event to the day view. Then you can read event.source.day/event.source.week/event.source.month inside the click-event.
If that doesn't help please add some example code to your question.

Related

Three JS device motion glitch

I'm trying to get into WebXR programming. I'm trying to make a simple wrapper which allows for a VR headset and also a Google Cardboard style for smartphones. I got the VR headset working good so far, but I have two issues when using the smartphone in landscape mode. Portrait mode works fine.
The camera breaks when going above the horizon. ** fixed **
Tilting the phone doesn't tilt. It pans left right half way.
Codesandbox.io code: https://codesandbox.io/s/webxr-7vw5q6
Codesandbox.io app: https://7vw5q6.csb.app/
Update
I managed to fix the jumping and flipped image in landscape mode by adding some code.
if(rotType == "YZX")
{
if(orientation_g >= 0) {
screenOrientation = -90;
} else {
screenOrientation = 90;
orientation_a = orientation_a + 180;
}
}
However, I still have the issue when rolling the device left or right.
// Variables
// ---------
let camera, renderer, scene, loop;
let container;
let controls;
let controller1, controller2;
let teleportmarker, raycaster, INTERSECTION;
let baseReferenceSpace;
let tempMatrix = new THREE.Matrix4();
let effect;
let action;
// fakeVR
let fakeVR = false;
let orientation_a, orientation_b, orientation_g;
/* landscape fix test */
let alphaOffset = 0;
let screenOrientation = 0;
/* ====================================================================================================
* Controller
* ==================================================================================================== */
class Controller {
constructor(i) {
this.controllers = this.createController(i);
this.controllerGrip = this.createControllerGrip(i);
if(i == 0) { this.controllers.name = "Right"; }
if(i == 1) { this.controllers.name = "Left"; }
this.axes = new THREE.Vector2();
this.viewDirection = camera.getWorldDirection( new THREE.Vector3() );
const scope = this;
raycaster = new THREE.Raycaster();
this.initInputListenerXR();
this.group = new THREE.Group();
this.teleportmarker = new THREE.Mesh(
new THREE.RingGeometry(0.2, 0.25, 32).rotateX(-Math.PI / 2),
new THREE.MeshBasicMaterial({color: 0xFF00FF})
);
scene.add(this.teleportmarker);
}
createController(i) {
const controllers = renderer.xr.getController(i);
if (0) {
this.group.add(controllers);
controllers.visible = true;
}
return controllers;
}
createControllerGrip(i) {
const controllerModelFactory = new THREE.XRControllerModelFactory();
const controllerGrip = renderer.xr.getControllerGrip(i);
controllerGrip.add(controllerModelFactory.createControllerModel(controllerGrip));
return controllerGrip;
}
initInputListenerXR() {
const listenerFor = name => event => {
const cb = this._eventListeners[name];
if (cb) {
const uuid = event.target.uuid;
const cont = this.controllers;
if (cont && cont.uuid === uuid) cb(idx);
}
};
this._addSelectListener('selectstart', this.onSelectStart);
this._addSelectListener('selectend', this.onSelectEnd);
this._addSelectListener('connected', function(event) {
this.controllers.add(this.buildController(event.data));
this.controllers.children[0].visible = false;
});
this._addSelectListener( 'disconnected', function () {
this.controllers.remove(this.controllers.children[0]);
});
}
_addSelectListener(eventName, listener) {
this.controllers.addEventListener(eventName, listener.bind(this));
}
onSelectStart() {
console.log(this.controllers.name + ' was pressed.');
this.controllers.userData.isSelecting = true;
}
onSelectEnd() {
console.log(this.controllers.name + ' was released.');
this.controllers.userData.isSelecting = false;
if ( INTERSECTION ) {
const offsetPosition = { x: - INTERSECTION.x, y: - INTERSECTION.y, z: - INTERSECTION.z, w: 1 };
const offsetRotation = new THREE.Quaternion();
const transform = new XRRigidTransform( offsetPosition, offsetRotation );
const teleportSpaceOffset = baseReferenceSpace.getOffsetReferenceSpace( transform );
renderer.xr.setReferenceSpace( teleportSpaceOffset );
}
}
update() {
INTERSECTION = undefined;
if ( controller1.controllers.userData.isSelecting === true ) {
tempMatrix.identity().extractRotation( controller1.controllers.matrixWorld );
raycaster.ray.origin.setFromMatrixPosition( controller1.controllers.matrixWorld );
raycaster.ray.direction.set( 0, 0, - 1 ).applyMatrix4( tempMatrix );
const intersects = raycaster.intersectObjects([floor]);
if (intersects.length > 0) {
INTERSECTION = intersects[0].point;
}
}
else if ( controller2.controllers.userData.isSelecting === true ) {
tempMatrix.identity().extractRotation( controller2.controllers.matrixWorld );
raycaster.ray.origin.setFromMatrixPosition( controller2.controllers.matrixWorld );
raycaster.ray.direction.set( 0, 0, - 1 ).applyMatrix4( tempMatrix );
const intersects = raycaster.intersectObjects([floor]);
if (intersects.length > 0) {
INTERSECTION = intersects[0].point;
}
}
if (INTERSECTION) this.teleportmarker.position.copy(INTERSECTION);
this.teleportmarker.visible = INTERSECTION !== undefined;
}
// updateArc() {
// }
buildController(data) {
switch(data.targetRayMode) {
// case 'screen':
// return;
case 'tracked-pointer':
const geometry = new THREE.BufferGeometry();
geometry.setAttribute('position', new THREE.Float32BufferAttribute([0,0,0,0,0,-1],3));
geometry.setAttribute('color', new THREE.Float32BufferAttribute([0.5,0.5,0.5,0,0,0],3));
const material = new THREE.LineBasicMaterial({vertexColors:true,blending:THREE.AdditiveBlending});
return new THREE.Line(geometry,material);
case 'gaze':
const gaze_geometry = new THREE.RingGoemetry(0.02,0.04,32).translate(0,0,-1);
const gaze_material = new THREE.MeshBesicMaterial({opacity:0.5,transparent:true});
return new THREE.Mesh(gaze_geometry,gaze_material);
}
}
setAction(button, functionName) {
}
action(button, functionName) {
}
}
/* ====================================================================================================
* Resizer
* ==================================================================================================== */
class Resizer {
constructor(container, camera, renderer) {
this.setSize(container, camera, renderer);
window.addEventListener('resize', () => {
this.setSize(container, camera, renderer);
this.onResize();
});
}
onResize() {
}
setSize(container, camera, renderer) {
camera.aspect = container.clientWidth / container.clientHeight;
camera.updateProjectionMatrix();
renderer.setSize(container.clientWidth, container.clientHeight);
renderer.setPixelRatio(window.devicePixelRatio);
}
}
/* ====================================================================================================
* Camera
* ==================================================================================================== */
class Camera extends THREE.PerspectiveCamera {
constructor() {
super();
this.onCreate();
}
onCreate() {
new THREE.PerspectiveCamera(35, 1, 0.1, 100);
}
}
/* ====================================================================================================
* Model
* ==================================================================================================== */
class Model extends THREE.Group {
constructor(data) {
super();
this.modelUrl = data;
this.onCreate();
}
onCreate() {
const dracoLoader = new THREE.DRACOLoader();
dracoLoader.setDecoderPath( 'vendor/three/examples/js/libs/draco/' );
dracoLoader.setDecoderConfig({ type: 'js' });
new THREE.GLTFLoader().
setDRACOLoader( dracoLoader ).
load(this.modelUrl,
gltf => {
this.updateTransform();
this.add(gltf.scene);
console.log(this);
});
}
updateMaterials(model) {
model.traverse(child => {
child.material = new THREE.MeshNormalMaterial();
});
}
updateTransform() {
}
dispose() {
}
rotate(x, y, z) {
this.rotation.x = THREE.MathUtils.degToRad(x);
this.rotation.y = THREE.MathUtils.degToRad(y);
this.rotation.z = THREE.MathUtils.degToRad(z);
}
scale(x, y, z) {
this.scale.set(x, y, z);
}
}
/* ====================================================================================================
* Loop
* ==================================================================================================== */
const clock = new THREE.Clock();
class Loop {
constructor(camera, scene, renderer) {
this.updatables = [];
}
start() {
renderer.setAnimationLoop(() => {
this.tick();
if(fakeVR) {
effect.render( scene, camera );
} else {
renderer.render(scene, camera);
}
});
}
stop() {
renderer.setAnimationLoop(null);
}
tick() {
const delta = clock.getDelta();
controller1.update();
controller2.update();
}
}
/* ====================================================================================================
* Scene
* ==================================================================================================== */
class Scene extends THREE.Scene {
constructor() {
super();
this.onCreate();
}
onCreate() {
new THREE.Scene();
this.background = new THREE.Color('skyblue');
}
createLights() {
const ambientLight = new THREE.HemisphereLight(
'white',
'darkslategrey',
5
);
const mainLight = new THREE.DirectionalLight('white', 4);
mainLight.position.set(100, 100, 100);
return { ambientLight, mainLight };
}
}
/* ====================================================================================================
* Application
* ==================================================================================================== */
class App {
constructor(i) {
// Setup <body> CSS style
document.getElementsByTagName("body")[0].style.cssText = 'width: 100vw; height: 100vh; margin: 0; padding: 0; overflow:
hidden;';
// Create VR scene <div>
const VRdiv = document.createElement('div');
VRdiv.id = "VRScene";
VRdiv.style.cssText = 'position: absolute; width: 100vw; height: 100vh; display: block;';
document.body.insertAdjacentElement('afterbegin', VRdiv);
// Controls
controls = new Controls();
// Setup Camera
camera = new Camera();
camera.position.set(0, 0, 0);
camera.up.set(0, 1, 0);
renderer = this.createRenderer();
scene = new Scene();
loop = new Loop(camera, scene, renderer);
container = document.querySelector('#VRScene');
container.append(renderer.domElement);
const { ambientLight, mainLight } = scene.createLights();
loop.updatables.push(controls);
scene.add(ambientLight, mainLight);
const resizer = new Resizer(container, camera, renderer);
this.init(i);
this.start();
}
init(i) {
this.setupXR(i);
}
setupXR(i) {
renderer.xr.addEventListener("sessionstart", () => (baseReferenceSpace = renderer.xr.getReferenceSpace()));
document.write(`
<button id='VRIcon' class='toggleVR' style=" position: fixed; bottom: 10px; left: 10px; outline: none; border:
none; background: none; width: 60px; z-index: 10000;" onclick='` + i
+ `.toggleVR()' title='Toggle VR Mode for Mobile Devices Only'>
<svg style="width: 100%; fill: white; stroke: rgba(0,0,0,0.25);" xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" x="0px"
y="0px" viewBox="0 0 62.7 52.375" enable-background="new 0 0 62.7
41.9" xml:space="preserve"><path d="M53.4,5.5h-44c-2.1,0-3.7,1.7-3.7,3.7v22.6c0,2.1,1.7,3.7,3.7,3.7h13.4c1.1,0,2.1-0.6,2.5-1.6l3-7.5c1.2-2.6,4.9-2.5,6,0.1
l2.6,7.3c0.4,1,1.4,1.7,2.5,1.7h13.9c2.1,0,3.7-1.7,3.7-3.7V9.3C57.2,7.2,55.5,5.5,53.4,5.5z
M20.4,27c-3.2,0-5.7-2.6-5.7-5.7
s2.6-5.7,5.7-5.7s5.7,2.6,5.7,5.7S23.6,27,20.4,27z
M42.4,27c-3.2,0-5.7-2.6-5.7-5.7s2.6-5.7,5.7-5.7s5.7,2.6,5.7,5.7
S45.6,27,42.4,27z"/></svg>
</button>
<svg id="VROverlay" xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="none
meet" width="100vw" height="100vh" viewBox="0, 0, 2000, 1000"
style="position: absolute; top: 0; left: 0; bottom: 0; right: 0;
z-index: 9999; display: none;"><g id="svgg"><path id="path0" d="M 0 0
L 0 1000 L 1000000 1000 L 1000000 0 L 0 0 z M 500.04492 15 C
636.69612 15.006191 768.82704 43.380704 892.76562 99.34375 C 896.20268 100.89576 898.95249 103.64562 900.50391 107.08398 C 1013.1637 356.78574 1013.1657 643.21219 900.50781 892.91602 C 898.9564 896.35438 896.20466 899.10424 892.76758 900.65625 C 768.82901 956.61724 636.69909 984.9898 499.95508 985 C 363.30182 984.99379 231.171 956.61724 107.23242 900.65625 C 103.79536 899.10424 101.04557 896.35438 99.494141 892.91602 C -13.163603 643.21219 -13.163603 356.78574 99.494141 107.08398 C 101.04557 103.64562 103.79536 100.89576 107.23242 99.34375 C 231.171 43.380704 363.3009 15.0062 500.04492 15 z M 1500.0449 15 C 1636.6961 15.006191 1768.827 43.380704 1892.7656 99.34375 C 1896.2026 100.89576 1898.9525 103.64562 1900.5039 107.08398 L 1900.5078 107.08398 C 2013.1656 356.78574 2013.1656 643.21219 1900.5078 892.91602 C 1898.9564 896.35438 1896.2047 899.10424 1892.7676 900.65625 C 1768.8291 956.61724 1636.6991 984.9898 1499.9551 985 C 1363.3019 984.99379 1231.1709 956.61724 1107.2324 900.65625 C 1103.7953 899.10424 1101.0455 896.35438 1099.4941 892.91602 C 986.8364 643.21219 986.8364 356.78574 1099.4941 107.08398 C 1101.0455 103.64562 1103.7953 100.89576 1107.2324 99.34375 C 1231.1709 43.380704 1363.3009 15.0062 1500.0449 15 z " stroke="none" fill="#000000" fill-rule="evenodd"></path></g></svg>
`);
if ('xr' in navigator) {
navigator.xr.isSessionSupported('immersive-vr').then(function(supported)
{
if(supported) {
renderer.xr.enabled = true;
new THREE.VRButton(renderer);
document.body.appendChild(THREE.VRButton.createButton(renderer));
document.getElementById('VRButton').style.display = 'block';
document.getElementById('VRIcon').style.display = 'block';
}
});
}
controller1 = new Controller(0);
controller2 = new Controller(1);
scene.add(controller1.controllers);
scene.add(controller1.controllerGrip);
scene.add(controller2.controllers);
scene.add(controller2.controllerGrip);
controls = new Controls();
action = new Action();
// loop.tick();
}
start() {
loop.start();
}
stop() {
loop.stop();
}
createRenderer() {
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setPixelRatio( window.devicePixelRatio );
renderer.physicallyCorrectLights = true;
effect = new THREE.StereoEffect( renderer );
effect.setSize( window.innerWidth, window.innerHeight );
return renderer;
}
toggleVR() {
if(DeviceMotionEvent && typeof DeviceMotionEvent.requestPermission === "function") {
DeviceMotionEvent.requestPermission();
}
if (fakeVR) {
fakeVR = false;
document.getElementById('VROverlay').style.display = 'none';
window.removeEventListener("deviceorientation", this.handleOrientation);
} else {
fakeVR = true;
window.addEventListener("deviceorientation", this.handleOrientation);
document.getElementById('VROverlay').style.display = 'block';
}
loop.stop();
loop.start();
}
handleMotion(event) {
}
handleOrientation(event) {
if (window.screen.orientation) {
screenOrientation = window.screen.orientation.angle;
} else if (typeof window.orientation === "number") {
screenOrientation = window.orientation;
} else if (window.screen.mozOrientationn) {
screenOrientation = {
"portrait-primary": 0,
"portrait-secondary": 180,
"landscape-primary": 90,
"landscape-secondary": 270,
}[window.screen.mozOrientation];
}
var eyem = new THREE.Quaternion().setFromEuler(new THREE.Euler(-Math.PI / 2, 0, 0));
var d2r = Math.PI / 180;
orientation_a = event.alpha;
orientation_b = event.beta;
orientation_g = event.gamma;
var rotType = (screenOrientation === 0 || screenOrientation === 180) ? "YXZ" : "YZX";
if(rotType == "YZX")
{
if(orientation_g >= 0) {
screenOrientation = -90;
} else {
screenOrientation = 90;
orientation_a = orientation_a + 180;
}
}
var rotm = new THREE.Quaternion().setFromEuler(
new THREE.Euler(orientation_b * d2r, orientation_a * d2r, -orientation_g * d2r, rotType)
);
var devm = new THREE.Quaternion().setFromEuler(
new THREE.Euler(0, -screenOrientation * d2r, 0)
);
rotm.multiply(devm).multiply(eyem); //rot = (rot x dev) x eye
camera.quaternion.copy(rotm);
document.getElementById("Orientation_a1").innerHTML = orientation_a.toFixed(3);
document.getElementById("Orientation_b1").innerHTML = orientation_b.toFixed(3);
document.getElementById("Orientation_g1").innerHTML = orientation_g.toFixed(3);
document.getElementById("Orientation_o1").innerHTML = screenOrientation;
document.getElementById("Orientation_a2").innerHTML = orientation_a.toFixed(3);
document.getElementById("Orientation_b2").innerHTML = orientation_b.toFixed(3);
document.getElementById("Orientation_g2").innerHTML = orientation_g.toFixed(3);
document.getElementById("Orientation_o2").innerHTML = screenOrientation;
}
}
ul {
padding-inline-start: 15px;
}
li {
list-style-type: none;
overflow: hidden;
}
<script src="https://7vw5q6.csb.app/webxr.three.js"></script>
<div style="display: block; position: fixed; top: 10%; left: 10%; right: 60%; background: rgba(255,255,255,.65);">
<ul>
<li>X-axis (β): <span id="Orientation_b1">0</span><span>°</span></li>
<li>Y-axis (γ): <span id="Orientation_g1">0</span><span>°</span></li>
<li>Z-axis (α): <span id="Orientation_a1">0</span><span>°</span></li>
<li>Orientation: <span id="Orientation_o1">0</span><span>°</span></li>
</ul>
</div>
<div style="display: block; position: fixed; top: 10%; left: 60%; right: 10%; background: rgba(255,255,255,.65);">
<ul>
<li>X-axis (β): <span id="Orientation_b2">0</span><span>°</span></li>
<li>Y-axis (γ): <span id="Orientation_g2">0</span><span>°</span></li>
<li>Z-axis (α): <span id="Orientation_a2">0</span><span>°</span></li>
<li>Orientation: <span id="Orientation_o2">0</span><span>°</span></li>
</ul>
</div>

Is there a way to "simulate" pressing the refresh button to refresh a List?

Is there a way to "simulate" pressing the refresh button to refresh a List? I have a list that I want it to update every 10 seconds. Is there a way to "press" the refresh button every 10 seconds?
My list name is ActiveJobsList.
This is what I have at the moment:
export function autoRefresh() {
var counter = 10;
var id;
if(location.href.includes("activejobs")) {
id = setInterval(function() {
counter--;
if(counter < 0 && location.href.includes("activejobs")) {
// What should go here?
clearInterval(id);
}
}, 1000);
}
else if (!location.href.includes("activejobs"))
{
clearInterval(id);
}
}
Okay so I managed to figure it out.
I used
var x = document.getElementsByTagName('button');
console.log(x);
To figure out which button corresponded to the refresh button for admin-on-rest. In my case, it was the second button in the array.
Here is my updated code.
export function autoRefresh() {
var counter = 30;
var id;
if(location.href.includes("activejobs")) {
id = setInterval(function() {
counter--;
if(counter < 0 && location.href.includes("activejobs")) {
document.getElementsByTagName('button')[1].click();
counter = 30;
}
}, 1000);
}
else if (!location.href.includes("activejobs"))
{
counter = 30;
}
}
You could leverage React.Component.shouldComponentUpdate(), on your ActiveJobsList
https://facebook.github.io/react/docs/react-component.html#shouldcomponentupdate
I have created a component that provides a drop down menu for auto update setting. Here is the code and below it is an example of how to invoke it.
class AutoUpdt extends Component {
static propTypes = { setAutoUpdate : PropTypes.func
, interval : PropTypes.array
, iconColor : PropTypes.any
}
static defaultProps = { interval : [10,30,60,120,300,600,900,1800,3600]
, iconColor : '#00bcd4'
}
constructor(props) { super(props)
this.state = { open : false
, needrefresh : false
, intervaltime : false
}
}
handleTouchTap(event) { event.preventDefault()
this.setState({ open: true, anchorEl: event.currentTarget, })
}
handleRequestClose() { this.setState({ open: false, })
}
handleShow(event) { let intervaltime = event.currentTarget.innerText.toLowerCase().split(' (secs)')[0].trim()
let newintevaltime = (this.state.intervaltime === false) ? intervaltime : false
this.props.setAutoUpdate( newintevaltime )
this.setState({ open: false, needrefresh: true, intervaltime : newintevaltime})
}
render() {
return ( <div style={{ display: 'inline-block' }}>
<IconButton tooltip="Set Auto Update"
iconStyle={{ color: this.props.iconColor }}
onTouchTap={this.handleTouchTap.bind(this)} ><AutoIcon /></IconButton>
<Popover open={this.state.open}
anchorEl={this.state.anchorEl}
anchorOrigin={{ horizontal: 'left', vertical: 'bottom' }}
targetOrigin={{ horizontal: 'left', vertical: 'top' }}
onRequestClose={this.handleRequestClose.bind(this)} >
<Menu>
{this.props.interval.map( el =>
<ListItem style={( el.toString() !== this.state.intervaltime )
? { color:'#00bcd4' , margin: 0, padding : 2 }
: { color: '#f48fb1' , margin: 0, padding : 2 } }
data-key={ el.toString()}
key={el.toString()}
primaryText={ el.toString() + ' (secs)'}
onTouchTap={this.handleShow.bind(this)} /> )}
</Menu >
</Popover>
</div>)
}
}
// It is invoked by using these two functions in another component
checkMounted(){ this.props.checkMounted && this.props.checkMounted() && this.updateData()
}
setAutoUpdate = ( intervaltimer, checkMounted) => {
const this_ = this
this.state.intervaltimer && clearInterval(this.state.intervaltimer)
this.setState( intervaltimer ? { intervaltimer : setInterval( this_.checkMounted.bind(this_), +intervaltimer * 1000) } : { intervaltimer : false} )
}
// And using this line in the render function of the calling component
{ this.props.hasAuto && <AutoUpdt setAutoUpdate={this.setAutoUpdate} icon={<NavigationRefresh />} /> }

Kendo treelist - trying to set a column template

I'm working with a Kendo treelist widget, and disappointed to see there's no rowTemplate option as there is on the Kendo grid.
I see a columnTemplate option (i.e. http://docs.telerik.com/kendo-ui/api/javascript/ui/treelist#configuration-columns.template ), but this will affect the entire column.
However, I need to drill into each cell value and set a css color property based on a ratio ( i.e. If value/benchmark < .2, assign <span style='color:red;'> , but my color value is dynamic.
There's a dataBound: and dataBinding: event on the treelist, but I'm still trying to figure out how to intercept each cell value and set the color once I've done my calculation.
var treeOptions = {
dataSource: ds,
columns: colDefs,
selectable: true,
scrollable: true,
resizable: true,
reorderable: true,
height: 320,
change: function (e) {
// push selected dataItem
var selectedRow = this.select();
var row = this.dataItem(selectedRow);
},
dataBound: function (e) {
console.log("dataBinding");
var ds = e.sender.dataSource.data();
var rows = e.sender.table.find("tr");
}
};
and this is where I'm building out the `colDefs' object (column definitions):
function parseHeatMapColumns(data, dimId) {
// Creates the Column Headers of the heatmap treelist.
// typeId=0 is 1st Dimension; typeId=1 is 2nd Dimension
var column = [];
column.push({
"field": "field0",
"title": "Dimension",
headerAttributes: { style: "font-weight:" + 'bold' + ";" },
attributes : { style: "font-weight: bold;" }
});
var colIdx = 1; // start at column 1 to build col headers for the 2nd dimension grouping
_.each(data, function (item) {
if (item.typeId == dimId) {
// Dimension values are duplicated, so push unique values (i.e. trade types may have dupes, whereas a BkgLocation may not).
var found = _.find(column, { field0: item.field0 });
if (found == undefined) {
column.push({
field: "field2",
title: item.field0,
headerAttributes: {
style: "font-weight:" + 'bold'
}
,template: "<span style='color:red;'>#: field2 #</span>"
});
colIdx++;
}
}
});
return column;
}
**** UPDATE ****
In order to embed some logic within the template :
function configureHeatMapColumnDefs(jsonData, cols, model) {
var colDef = '';
var dimId = 0;
var colorProp;
var columns = kendoGridService.parseHeatMapColumns(jsonData, dimId);
// iterate columns and set color property; NB: columns[0] is the left-most "Dimension" column, so we start from i=1.
for (var i = 1; i <= columns.length-1; i++) {
columns[i]['template'] = function (data) {
var color = 'black';
if (data.field2 < 1000) {
color = 'red';
}
else if (data.field2 < 5000) {
color = 'green';
}
return "<span style='color:" + color + ";'>" + data.field2 + "</span>";
};
}
return columns;
}
Advice is appreciated.
Thanks,
Bob
In the databound event you can iterate through the rows. For each row you can get the dataItem associated with it using the dataitem() method (http://docs.telerik.com/kendo-ui/api/javascript/ui/treelist#methods-dataItem)
Once you have the dataitem, calculate your ration and if the row meets the criteria for color, change the cell DOM element:
dataBound: function (e) {
var that = e.sender;
var rows = e.sender.table.find("tr");
rows.each(function(idx, row){
var dataItem = that.dataItem(row);
var ageCell = $(row).find("td").eq(2);
if (dataItem.Age > 30) {
//mark in red
var ageText = ageCell.text();
ageCell.html('<span style="color:red;">' + ageText + '</span>');
}
}
DEMO
UPDATE: you can also do this with a template:
$("#treelist").kendoTreeList({
dataSource: dataSource,
height: 540,
selectable: true,
columns: [
{ field: "Position"},
{ field: "Name" },
{ field: "Age",
template: "# if ( data.Age > 30 ) { #<span style='color:red;'> #= data.Age # </span> #}else{# #= data.Age # #}#"
}
],
});
DEMO

Kendo UI Slider / how to disable keyboard input?

Is there a way to disable the keyboard events on Kendo UI Slider? Basically, I want to prevent changing the value of the slider when pressing left and right arrow keys. Is this possible at all?
Please note that slider is being dynamically inserted into the DOM as part of a KO custom binding handler.
ko.bindingHandlers.tone = {
init: function(element, valueAccessor) {
if (valueAccessor().settingToneEnabled) {
var $el = $(element);
var tag = '<span class="dropdown mrgn-tp-md"><ul class="dropdown-menu dropdown-menu-right text-center pddng-sm" aria-labelledby="tonedropdownMenu"><li class="pddng-lft-md pddng-rght-sm"><span id="tone-slider" title="tone"></span></li><li class="pddng-rght-sm"><i class="icon icon-delete"></i> ' + i18n['ps-deleteArticleToneLabel'] + '</li></ul></span>';
$(tag).appendTo($el);
var $slider = $('#tone-slider', $el);
var $delLink = $('a.del', $el);
var $dropdown = $('span.dropdown', $el);
$('a.dropdown-toggle', $dropdown).on('click', function() {
$('.dropdown-menu', $dropdown).toggle();
});
$slider.kendoSlider({
change: function(e) {
var va = valueAccessor();
va.value(e.value);
if ($.isFunction(va.handleUserInput)) {
va.handleUserInput();
}
},
showButtons: false,
min: -1,
max: 1,
smallStep: 1,
value: valueAccessor().value() || 0,
tickPlacement: 'none',
tooltip: {
enabled: false
}
});
$('.k-draghandle', $el).off('keydown');
$delLink.on('click', function(e) {
e.preventDefault();
if ($delLink.attr('disabled')) {
return;
}
var va = valueAccessor();
va.value(null);
if ($.isFunction(va.handleUserInput)) {
va.handleUserInput();
}
});
$el.data('slider', $slider.data("kendoSlider"));
$el.data('deleteButton', $delLink);
$el.data('dropdown', $dropdown);
} else {
$('<span href="" data-tone></span>').appendTo(element);
}
},
update: function(element, valueAccessor) {
var toneValues = {
'1': {
name: i18n['ps-tonePositive'],
val: 1,
css: 'icon-tone-positive'
},
'0': {
name: i18n['ps-toneNeutral'],
val: 0,
css: 'icon-tone-neutral'
},
'-1': {
name: i18n['ps-toneNegative'],
val: 0,
css: 'icon-tone-negative'
},
};
var $tone = $('*[data-tone]', element);
var val = valueAccessor().value() || 0;
var tone = toneValues[val.toString()] || toneValues['0'];
$tone.removeClass()
.addClass('icon').addClass(tone.css)
.attr('title', tone.name);
if (valueAccessor().settingToneEnabled) {
$('#tone-slider', element).data("kendoSlider").value(val);
}
},
};
You can try to remove the keydown handler.
See demo.

Cloning objects in Fabric.js

I am cloning a selected object on a canvas in Fabric.js using a simple function.
function duplicateObj() {
var obj = canvas.getActiveObject();
var clone = fabric.util.object.clone(obj);
clone.set({left: 100,top: 100});
canvas.add(clone);
}
That works absolutely fine. Now if I work with the object and the clone is not required anymore and I select and delete it, both objects, the clone and the original object are deleted. The delete function is:
function deleteObj() {
var obj = canvas.getActiveObject();
canvas.fxRemove(obj);
}
The objects are the same. Is there are way to clone objects and make the clone independent of the of the original? I tried this:
function duplicateObj() {
var obj = canvas.getActiveObject();
var clone = fabric.util.object.clone(obj);
clone.initialize();
$.extend(clone, obj);
fabric.util.object.extend(clone, obj);
clone.set({left: 100,top: 100});
canvas.add(clone);
}
It works, however the objects are the same again and if I only use initialize I am ending up with an object that has now properties set.
here is the solution
var object = fabric.util.object.clone(canvas.getActiveObject());
object.set("top", object.top+5);
object.set("left", object.left+5);
canvas.add(object);
This worked very well for me, and the cloned object is totally unlinked from the original:
var object = canvas.getActiveObject();
object.clone(function(clone) {
canvas.add(clone.set({
left: object.left + 10,
top: object.top + 10
}));
});
And you can do it to clone all selected objects:
var activeObjects = canvas.getActiveObjects();
if (activeObjects) {
activeObjects.forEach(function(object) {
object.clone(function(clone) {
canvas.add(clone.set({
left: object.left + 10,
top: object.top + 10
}));
})
});
}
I hope it can help you!
I was having a similar issue where actions on the clone would affect the original object. I opted to just serialize the object and deserialize it into a new object:
var copyData = canvas.getActiveObject().toObject();
fabric.util.enlivenObjects([copyData], function(objects) {
objects.forEach(function(o) {
o.set('top', o.top + 15);
o.set('left', o.left + 15);
canvas.add(o);
});
canvas.renderAll();
});
for fabricjs 2.0
$(".copy").on("click", function () {
var activeObject = canvas.getActiveObject();
activeObject.clone(function (cloned) {
canvas.discardActiveObject();
cloned.set({
top: cloned.top + 20,
evented: true
});
if (cloned.type === 'activeSelection') {
// active selection needs a reference to the canvas.
cloned.canvas = canvas;
cloned.forEachObject(function (obj) {
canvas.add(obj);
});
cloned.setCoords();
} else {
canvas.add(cloned);
}
canvas.setActiveObject(cloned);
canvas.requestRenderAll();
});
});
Here is my implementation of cloning selected object or group.
https://jsfiddle.net/milanhlinak/rxtjm7w0/1/
<!DOCTYPE html>
<html>
<head>
<script type="text/javascript" src="lib/jquery-3.1.1.min.js"></script>
<script type="text/javascript" src="lib/fabric.min.js"></script>
</head>
<body>
<button onclick="cloneSelected()">Clone selected</button>
<canvas id="canvas" style="border: 1px solid #cccccc"></canvas>
<script>
var canvas = new fabric.Canvas('canvas', {
width: 500,
height: 500,
});
canvas.add(new fabric.Rect({
left: 100,
top: 100,
width: 50,
height: 50,
fill: '#faa'
}));
canvas.add(new fabric.Circle({
left: 300,
top: 300,
radius: 25,
fill: '#afa'
}));
function cloneSelected() {
console.log('cloneSelected');
var activeObject = canvas.getActiveObject();
var activeGroup = canvas.getActiveGroup();
if (activeObject) {
console.log('object selected');
var clonedObject = null;
var json = activeObject.toJSON();
if (json.type == 'rect') {
clonedObject = new fabric.Rect(json);
} else if (json.type == 'circle') {
clonedObject = new fabric.Circle(json);
} else {
console.log('unknown object type: ' + json.type);
return;
}
var oldLeft = clonedObject.getLeft();
var oldTop = clonedObject.getTop();
clonedObject.setLeft(oldLeft + 10);
clonedObject.setTop(oldTop + 10);
var boundingRect = clonedObject.getBoundingRect(true);
if (boundingRect.left + boundingRect.width > canvas.getWidth()) {
clonedObject.setLeft(oldLeft);
}
if (boundingRect.top + boundingRect.height > canvas.getHeight()) {
clonedObject.setTop(oldTop);
}
canvas.add(clonedObject);
canvas.setActiveObject(clonedObject);
canvas.renderAll();
console.log('selected object cloned');
} else if (activeGroup) {
console.log('group selected');
canvas.discardActiveGroup();
var clonedObjects = [];
activeGroup.getObjects().forEach(function (object) {
var clonedObject = null;
var json = object.toJSON();
if (json.type == 'rect') {
clonedObject = new fabric.Rect(json);
} else if (json.type === 'circle') {
clonedObject = new fabric.Circle(json);
} else {
console.log('unknown object type: ' + json.type);
return;
}
clonedObject.setCoords();
canvas.add(clonedObject);
clonedObject.set('active', true);
clonedObjects.push(clonedObject);
});
var group = new fabric.Group(clonedObjects.reverse(), {
canvas: canvas
});
group.addWithUpdate(null);
var oldLeft = group.getLeft();
var oldTop = group.getTop();
group.setLeft(oldLeft + 10);
group.setTop(oldTop + 10);
var boundingRect = group.getBoundingRect(true);
if (boundingRect.left + boundingRect.width > canvas.getWidth()) {
group.setLeft(oldLeft);
}
if (boundingRect.top + boundingRect.height > canvas.getHeight()) {
group.setTop(oldTop);
}
group.setCoords();
canvas.setActiveGroup(group);
group.saveCoords();
canvas.renderAll();
console.log('selected objects cloned');
} else {
console.log('no object selected');
}
}
</script>
</body>
</html>
You can use
var obj = canvas.getActiveObject();
obj.clone(function(c) {
canvas.add(c.set({ left: 100, top: 100, angle: -15 }));
});
Here you can see it working:
http://fabricjs.com/opacity_mouse_move/
I wanted a clone to be able to reset an item to the originally saved state...like others I found objects pass on variables to the clone and vice versa...
What i did was create a var to hold the clone for later recall like this:
var o;
var object = canvas.getActiveObject().clone(
function(obj){
o = obj;
}
);
one caveat if you set id's and dont make them unique during cloning process it makes the whole thing go wild...unless you delete the original first..
Check the demo for Copy and Paste here:
http://fabricjs.com/copypaste
Here is the code to copy/paste or clone the selected object.
function Clone() {
Copy();
Paste()
}
function Copy() {
// clone what are you copying since you
// may want copy and paste on different moment.
// and you do not want the changes happened
// later to reflect on the copy.
canvas.getActiveObject().clone(function(cloned) {
_clipboard = cloned;
});
}
function Paste() {
// clone again, so you can do multiple copies.
_clipboard.clone(function(clonedObj) {
canvas.discardActiveObject();
clonedObj.set({
left: clonedObj.left + 10,
top: clonedObj.top + 10,
evented: true,
});
if (clonedObj.type === 'activeSelection') {
// active selection needs a reference to the canvas.
clonedObj.canvas = canvas;
clonedObj.forEachObject(function(obj) {
canvas.add(obj);
});
// this should solve the unselectability
clonedObj.setCoords();
} else {
canvas.add(clonedObj);
}
_clipboard.top += 10;
_clipboard.left += 10;
canvas.setActiveObject(clonedObj);
canvas.requestRenderAll();
});
}

Resources