I am struggling to get the HTML data from the sourcedialog in ckeditor.
I can get the HTML data from the editor itself, no problem. But getting it from the dialog is a pain in the ass.
I want to display a live preview of the HTML entered in the source dialog, and for that I need the HTML data, not from the editor, but from the dialog which the user is editing.
CKEDITOR.on('dialogDefinition', function(ev) {
var editor = ev.editor;
var dialog = ev.data.definition.dialog;
var dialogName = ev.data.name;
var dialogDefinition = ev.data.definition;
var editorName = ev.editor.name;
var htmlData = CKEDITOR.instances[editorName].getData();
if (dialogName == 'sourcedialog') {
dialog.on('show', function () {
//console.log(this.getSize().width);
console.log(this);
$("#sourcePreview").css("display", "block");
$("#sourcePreview").html(htmlData);
$(".cke_dialog_ui_input_textarea textarea").on("keyup", function() {
//var dialogElementUpdated = dialogObj.getElement().getFirst();
//console.log(editorData);
//$("#sourcePreview").html(htmlDataUpdated);
});
});
dialog.on('hide', function () {
console.log('dialog ' + dialogName + ' closed.');
$("#sourcePreview").css("display", "none");
});
}
});
This is what I have so far (sorry about all the console.logs, this is work in progress). I am obviously getting HTML data from the varible: htmlData, but this is from the editor, not the dialog.
CKEditor is great but yeah, there's a lot about it that's a pain in the ass.
if (dialogName == 'sourcedialog')
{
dialog.on('show', function ()
{
// 'input' is the correct event to listen to for a textarea,
// it fires on paste too.
this.getContentElement('main', 'data').getInputElement().on('input', function()
{
console.log('textarea contents: ' + this.$.value);
}
}
}
Related
I have followed Philippe Leefsma's tutorial on how to implement the markup tool, but without any luck. Link here: http://adndevblog.typepad.com/cloud_and_mobile/2016/02/playing-with-the-new-view-data-markup-api.html
and here: https://developer.api.autodesk.com/viewingservice/v1/viewers/docs/tutorial-feature_markup.html
I get errors that I need to include requireJS, but I don't want to use it. So instead I used this script in my html file:
<script src="https://autodeskviewer.com/viewers/2.2/extensions/MarkupsCore.js">
I don't know if this is the right way to go? I get no errors in the console, but the markup button doesn't show up in the dockingpanel.
This is my code for loading the extension in the viewer:
viewerApp = null;
function initializeViewer(containerId, urn, params) {
function getToken(url) {
return new Promise(function (resolve, reject) {
$.get(url, function (response) {
resolve(response.access_token);
});
});
}
var initOptions = {
documentId: 'urn:' + urn,
env: 'AutodeskProduction',
getAccessToken: function (onGetAccessToken) {
getToken(params.gettokenurl).then(function (val) {
var accessToken = val;
var expireTimeSeconds = 60 * 30;
onGetAccessToken(accessToken, expireTimeSeconds);
});
}
}
function onDocumentLoaded(doc) {
var rootItem = doc.getRootItem();
// Grab all 3D items
var geometryItems3d =
Autodesk.Viewing.Document.getSubItemsWithProperties(
rootItem, { 'type': 'geometry', 'role': '3d' }, true);
// Grab all 2D items
var geometryItems2d =
Autodesk.Viewing.Document.getSubItemsWithProperties(
rootItem, { 'type': 'geometry', 'role': '2d' }, true);
// Pick the first 3D item otherwise first 2D item
var selectedItem = (geometryItems3d.length ?
geometryItems3d[0] :
geometryItems2d[0]);
var domContainer = document.getElementById('viewerContainer');
var config = { extensions: ["Autodesk.Viewing.MarkupsCore"] };
// GUI Version: viewer with controls
var viewer = new Autodesk.Viewing.Private.GuiViewer3D(domContainer, config);
viewer.loadExtension("Autodesk.Viewing.MarkupsCore");
viewer.initialize();
viewer.loadModel(doc.getViewablePath(selectedItem));
var extension = viewer.getExtension("Autodesk.Viewing.MarkupsCore");
viewerApp = viewer;
}
function onEnvInitialized() {
Autodesk.Viewing.Document.load(
initOptions.documentId,
function (doc) {
onDocumentLoaded(doc);
},
function (errCode) {
onLoadError(errCode);
})
}
function onLoadError(errCode) {
console.log('Error loading document: ' + errCode);
}
Autodesk.Viewing.Initializer(
initOptions,
function () {
onEnvInitialized()
})
}
Any help is highly appreciated!
Unfortunately there has been a few changes to the API since I wrote that blog post. The MarkupCore.js is now included in the viewer3D.js source, so you don't need to reference any extra file or use requireJS if you use the latest version of the viewer API.
Keep in mind that this is an API-only feature, so even after loading the markup extension, you won't get any UI out of the box. You have to implemented it yourself, for example create a dialog with buttons that may eventually create markups by calling the API.
Some of the code from my blog post may still be valid and give you an idea about what you need to do.
Hope that helps.
I have a main folder with index.html file for my html app. I have written a code in index.html of main folder to access the file (index.html) present in the sub folder as follows,
SubFile
When i click on the above link, it is not navigating to the subfile and instead the link of main folder index.html file changes to mainfolder/index.html#!/subfolder/index.html
I even tried changing the name of subfolder file but no success. What could be the problem?
I also want to navigate back to the main folder index.html from subfolder as follow,
Mainfile
But it is also not working. How can I achieve this as well?
Edited:
The file my-app.js is creating the issue. The code of my-app.js is as follows,
// Initialize your app
var myApp = new Framework7({
animateNavBackIcon: true,
// Enable templates auto precompilation
precompileTemplates: true,
// Enabled pages rendering using Template7
swipeBackPage: false,
swipeBackPageThreshold: 1,
swipePanel: "left",
swipePanelCloseOpposite: true,
pushState: true,
pushStateRoot: undefined,
pushStateNoAnimation: false,
pushStateSeparator: '#!/',
template7Pages: true
});
// Export selectors engine
var $$ = Dom7;
// Add main View
var mainView = myApp.addView('.view-main', {
// Enable dynamic Navbar
dynamicNavbar: false
});
$$(document).on('pageInit', function (e) {
$(".swipebox").swipebox();
$("#ContactForm").validate({
submitHandler: function(form) {
ajaxContact(form);
return false;
}
});
$('a.backbutton').click(function(){
parent.history.back();
return false;
});
$(".posts li").hide();
size_li = $(".posts li").size();
x=4;
$('.posts li:lt('+x+')').show();
$('#loadMore').click(function () {
x= (x+1 <= size_li) ? x+1 : size_li;
$('.posts li:lt('+x+')').show();
if(x == size_li){
$('#loadMore').hide();
$('#showLess').show();
}
});
$("a.switcher").bind("click", function(e){
e.preventDefault();
var theid = $(this).attr("id");
var theproducts = $("ul#photoslist");
var classNames = $(this).attr('class').split(' ');
if($(this).hasClass("active")) {
// if currently clicked button has the active class
// then we do nothing!
return false;
} else {
// otherwise we are clicking on the inactive button
// and in the process of switching views!
if(theid == "view13") {
$(this).addClass("active");
$("#view11").removeClass("active");
$("#view11").children("img").attr("src","images/switch_11.png");
$("#view12").removeClass("active");
$("#view12").children("img").attr("src","images/switch_12.png");
var theimg = $(this).children("img");
theimg.attr("src","images/switch_13_active.png");
// remove the list class and change to grid
theproducts.removeClass("photo_gallery_11");
theproducts.removeClass("photo_gallery_12");
theproducts.addClass("photo_gallery_13");
}
else if(theid == "view12") {
$(this).addClass("active");
$("#view11").removeClass("active");
$("#view11").children("img").attr("src","images/switch_11.png");
$("#view13").removeClass("active");
$("#view13").children("img").attr("src","images/switch_13.png");
var theimg = $(this).children("img");
theimg.attr("src","images/switch_12_active.png");
// remove the list class and change to grid
theproducts.removeClass("photo_gallery_11");
theproducts.removeClass("photo_gallery_13");
theproducts.addClass("photo_gallery_12");
}
else if(theid == "view11") {
$("#view12").removeClass("active");
$("#view12").children("img").attr("src","images/switch_12.png");
$("#view13").removeClass("active");
$("#view13").children("img").attr("src","images/switch_13.png");
var theimg = $(this).children("img");
theimg.attr("src","images/switch_11_active.png");
// remove the list class and change to grid
theproducts.removeClass("photo_gallery_12");
theproducts.removeClass("photo_gallery_13");
theproducts.addClass("photo_gallery_11");
}
}
});
document.addEventListener('touchmove', function(event) {
if(event.target.parentNode.className.indexOf('navbarpages') != -1 || event.target.className.indexOf('navbarpages') != -1 ) {
event.preventDefault(); }
}, false);
// Add ScrollFix
var scrollingContent = document.getElementById("pages_maincontent");
new ScrollFix(scrollingContent);
var ScrollFix = function(elem) {
// Variables to track inputs
var startY = startTopScroll = deltaY = undefined,
elem = elem || elem.querySelector(elem);
// If there is no element, then do nothing
if(!elem)
return;
// Handle the start of interactions
elem.addEventListener('touchstart', function(event){
startY = event.touches[0].pageY;
startTopScroll = elem.scrollTop;
if(startTopScroll <= 0)
elem.scrollTop = 1;
if(startTopScroll + elem.offsetHeight >= elem.scrollHeight)
elem.scrollTop = elem.scrollHeight - elem.offsetHeight - 1;
}, false);
};
})
What shall i remove from it to solve my problem?
#!/subfolder/index.html
This make me feel that you are using a single page application framework/library, like Angular or something related. So maybe your problem is not in the html but in your javascript code.
Please remove all javascript and check it will work fine then revert all js one by one and test you will find the conflict javascript resolve that conflict. it will work fine.
I hope I am clear enough with this request for assistance, as it is hard to explain and I can't post all the code here. I have downloaded code to enable TinyMCE to be used in a NgRepeat with AngularJS:
angular.module('ui.tinymce', [])
.value('uiTinymceConfig', {})
.directive('uiTinymce', ['uiTinymceConfig', function (uiTinymceConfig) {
uiTinymceConfig = uiTinymceConfig || {};
var generatedIds = 0;
return {
require: 'ngModel',
link: function (scope, elm, attrs, ngModel) {
var expression, options, tinyInstance;
// generate an ID if not present
if (!attrs.id) {
attrs.$set('id', 'uiTinymce' + generatedIds++);
}
options = {
// Update model when calling setContent (such as from the source editor popup)
setup: function (ed) {
ed.on('init', function (args) {
ngModel.$render();
});
// Update model on button click
ed.on('ExecCommand', function (e) {
ed.save();
ngModel.$setViewValue(elm.val());
if (!scope.$$phase) {
scope.$apply();
}
});
// Update model on keypress
ed.on('KeyUp', function (e) {
ed.save();
ngModel.$setViewValue(elm.val());
if (!scope.$$phase) {
scope.$apply();
}
});
},
mode: 'exact',
elements: attrs.id
};
if (attrs.uiTinymce) {
expression = scope.$eval(attrs.uiTinymce);
} else {
expression = {};
}
angular.extend(options, uiTinymceConfig, expression);
setTimeout(function () {
tinymce.init(options);
});
ngModel.$render = function () {
if (!tinyInstance) {
tinyInstance = tinymce.get(attrs.id);
}
if (tinyInstance) {
tinyInstance.setContent(ngModel.$viewValue || '');
}
};
}
};
}]);
var gwApp = angular.module('gwApp', ['ui.tinymce']);
I don't really understand this code, but it works fine initially. My page starts with a list of Posts. I click on 'Show Reply' for the first post, and using NgSwitch the multiple replies become visible (nested NgRepeat). I submit a new reply message (the reply text is entered using tinymce) using a RESTful API service and a http call (too much code to post here). Then after clicking the submit button for the new reply message, the NgSwitch kicks in again unexpectedly to make the replies no longer visible. When I expand the replies again, the tinymce is just a regular textarea again, and the proper editor is gone.
I know this is not very clear, but I'm hoping someone can make sense of what I've written and can help me solve this problem..
I was having the same problem using ng-switch and ng-show so i added:
scope.$watch('onHidden()',function(){ tinymce.editors = [] });
after the setTimeout function.
Also replace the
ed.on('init',function(args){ ngModel.$render(); });
with
ed.on('init',function(args){ ed.setContent(ngModel.$viewValue); });
and remove the $render function.
This is the link to the working code in JsFiddle
I'm leveraging Google Chrome's omnibox API in my extension.
Current users, including myself, have noticed that the omnibox ceases responding entirely after an undetermined state change or a period of time lapsing. Typing the word to trigger entering into "omnibox" stops having any effect and the URL bar does not shift into omnibox mode.
Restarting Google Chrome does not fix the issue, but restarting my plugin by unchecking and then re-checking the 'enabled' checkbox on chrome://extensions does resolve the issue.
Does anyone have any suggestions on what to investigate? Below is the code used. It is only loaded once through my permanently persisted background page:
// Displays streamus search suggestions and allows instant playing in the stream
define([
'background/collection/streamItems',
'background/model/video',
'common/model/youTubeV2API',
'common/model/utility'
], function (StreamItems, Video, YouTubeV2API, Utility) {
'use strict';
console.log("Omnibox LOADED", chrome.omnibox);
var Omnibox = Backbone.Model.extend({
defaults: function () {
return {
suggestedVideos: [],
searchJqXhr: null
};
},
initialize: function () {
console.log("Omnibox INITIALIZED");
var self = this;
chrome.omnibox.setDefaultSuggestion({
// TODO: i18n
description: 'Press enter to play.'
});
// User has started a keyword input session by typing the extension's keyword. This is guaranteed to be sent exactly once per input session, and before any onInputChanged events.
chrome.omnibox.onInputChanged.addListener(function (text, suggest) {
// Clear suggested videos
self.get('suggestedVideos').length = 0;
var trimmedSearchText = $.trim(text);
// Clear suggestions if there is no text.
if (trimmedSearchText === '') {
suggest();
} else {
// Do not display results if searchText was modified while searching, abort old request.
var previousSearchJqXhr = self.get('searchJqXhr');
if (previousSearchJqXhr) {
previousSearchJqXhr.abort();
self.set('searchJqXhr', null);
}
var searchJqXhr = YouTubeV2API.search({
text: trimmedSearchText,
// Omnibox can only show 6 results
maxResults: 6,
success: function(videoInformationList) {
self.set('searchJqXhr', null);
var suggestions = self.buildSuggestions(videoInformationList, trimmedSearchText);
suggest(suggestions);
}
});
self.set('searchJqXhr', searchJqXhr);
}
});
chrome.omnibox.onInputEntered.addListener(function (text) {
// Find the cached video data by url
var pickedVideo = _.find(self.get('suggestedVideos'), function(suggestedVideo) {
return suggestedVideo.get('url') === text;
});
// If the user doesn't make a selection (commonly when typing and then just hitting enter on their query)
// take the best suggestion related to their text.
if (pickedVideo === undefined) {
pickedVideo = self.get('suggestedVideos')[0];
}
StreamItems.addByVideo(pickedVideo, true);
});
},
buildSuggestions: function(videoInformationList, text) {
var self = this;
var suggestions = _.map(videoInformationList, function (videoInformation) {
var video = new Video({
videoInformation: videoInformation
});
self.get('suggestedVideos').push(video);
var safeTitle = _.escape(video.get('title'));
var textStyleRegExp = new RegExp(Utility.escapeRegExp(text), "i");
var styledTitle = safeTitle.replace(textStyleRegExp, '<match>$&</match>');
var description = '<dim>' + video.get('prettyDuration') + "</dim> " + styledTitle;
return {
content: video.get('url'),
description: description
};
});
return suggestions;
}
});
return new Omnibox();
});
As far as I'm aware the code itself is fine and wouldn't have any effect on whether I see omnibox or not.
You can find full source code here: https://github.com/MeoMix/StreamusChromeExtension/blob/master/src/js/background/model/omnibox.js
I am trying to store the data a user enters inside a textarea in a popup.html. Using jQuery on window unload the data should be synced and on window ready the data should be restored. However, when opening popup.html the content of the textarea is undefined. This is the jQuery code which I am loading in popup.html:
$(window).unload (
function save() {
var textarea = document.querySelector("#contacts").value;
// Old method of storing data locally
//localStorage["contacts"] = textarea.value;
// Save data using the Chrome extension storage API.
chrome.storage.sync.set({contacts: textarea}, function() {
console.log("Contacts saved");
});
});
$(window).ready(
function restore() {
var textarea = document.querySelector("#contacts");
// Old method of retrieving data locally
// var content = localStorage["contacts"];
chrome.storage.sync.get('contacts', function(r) {
console.log("Contacts retrieved");
var content = r["contacts"];
textarea.value = content;
});
});
From popup.js you can invoke a method in background.js file to save the data:
popup.js:
addEventListener("unload", function(){
var background = chrome.extension.getBackgroundPage();
background.mySavefunction(data);
}
background.js:
function mySaveFunction(data){
chrome.storage.sync.set(data, function(){
console.log("Data saved.");
});
}
I found a solution. Instead of using $(window).unload() I now use a submit button which needs to be clicked before closing popup.html:
$("#save-button").click(function() {
var textarea = document.querySelector("#contacts").value;
var save = {};
save["contacts"] = textarea;
// Save data using the Chrome extension storage API.
chrome.storage.sync.set(save, function() {
console.log("Contacts saved");
});
$("#confirm").text("Contacts saved.").show().fadeOut(5000);
});