Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 58 additions & 11 deletions www/core/directives/external_content.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,34 +31,71 @@ angular.module('mm.core')
* Attributes accepted:
* - siteid: Reference to the site ID if different than the site the user is connected to.
*/
.directive('mmExternalContent', function($log, $mmFilepool, $mmSite, $mmSitesManager, $mmUtil) {
.directive('mmExternalContent', function($log, $mmFilepool, $mmSite, $mmSitesManager, $mmUtil, $q) {
$log = $log.getInstance('mmExternalContent');

/**
* Add a new source with a certain URL.
*
* @param {Object} dom Current source element. The new source will be a sibling of this element.
* @param {String} url URL to use in the source.
*/
function addSource(dom, url) {
if (dom.tagName !== 'SOURCE') {
return;
}

var e = document.createElement('source');
e.setAttribute('src', url);
e.setAttribute('type', dom.getAttribute('type'));
dom.parentNode.insertBefore(e, dom);
}

/**
* Handle external content, setting the right URL.
*
* @param {String} siteId Site ID.
* @param {Object} dom DOM element.
* @param {String} targetAttr Attribute to modify.
* @param {String} url Original URL to treat.
* @param {String} [component] Component
* @param {Number} [componentId] Component ID.
* @return {Promise} Promise resolved if the element is successfully treated.
*/
function handleExternalContent(siteId, dom, targetAttr, url, component, componentId) {

if (!url || !$mmUtil.isDownloadableUrl(url)) {
$log.debug('Ignoring non-downloadable URL: ' + url);
return;
if (dom.tagName === 'SOURCE') {
// Restoring original src.
addSource(dom, url);
}
return $q.reject();
}

// Get the webservice pluginfile URL, we ignore failures here.
$mmSitesManager.getSite(siteId).then(function(site) {
return $mmSitesManager.getSite(siteId).then(function(site) {
if (!site.canDownloadFiles() && $mmUtil.isPluginFileUrl(url)) {
dom.remove(); // Remove element since it'll be broken.
return;
return $q.reject();
}

var fn;

if (targetAttr === 'src') {
if (targetAttr === 'src' && dom.tagName !== 'SOURCE') {
fn = $mmFilepool.getSrcByUrl;
} else {
fn = $mmFilepool.getUrlByUrl;
}

fn(siteId, url, component, componentId).then(function(finalUrl) {
return fn(siteId, url, component, componentId).then(function(finalUrl) {
$log.debug('Using URL ' + finalUrl + ' for ' + url);
dom.setAttribute(targetAttr, finalUrl);
if (dom.tagName === 'SOURCE') {
// The browser does not catch changes in SRC, we need to add a new source.
addSource(dom, finalUrl);
} else {
dom.setAttribute(targetAttr, finalUrl);
}
});
});
}
Expand All @@ -70,20 +107,30 @@ angular.module('mm.core')
},
link: function(scope, element, attrs) {
var dom = element[0],
siteid = scope.siteid || $mmSite.getId(),
component = attrs.component,
componentId = attrs.componentId,
targetAttr,
observe = false,
url;
sourceAttr,
observe = false;

if (dom.tagName === 'A') {
targetAttr = 'href';
sourceAttr = 'href';
if (attrs.hasOwnProperty('ngHref')) {
observe = true;
}

} else if (dom.tagName === 'IMG') {
targetAttr = 'src';
sourceAttr = 'src';
if (attrs.hasOwnProperty('ngSrc')) {
observe = true;
}

} else if (dom.tagName === 'AUDIO' || dom.tagName === 'VIDEO' || dom.tagName === 'SOURCE') {
targetAttr = 'src';
sourceAttr = 'targetSrc';
if (attrs.hasOwnProperty('ngSrc')) {
observe = true;
}
Expand All @@ -99,10 +146,10 @@ angular.module('mm.core')
if (!url) {
return;
}
handleExternalContent(scope.siteid || $mmSite.getId(), dom, targetAttr, url, component, componentId);
handleExternalContent(siteid, dom, targetAttr, url, component, componentId);
});
} else {
handleExternalContent(scope.siteid || $mmSite.getId(), dom, targetAttr, attrs[targetAttr], component, componentId);
handleExternalContent(siteid, dom, targetAttr, attrs[sourceAttr] || attrs[targetAttr], component, componentId);
}

}
Expand Down
90 changes: 60 additions & 30 deletions www/core/directives/formattext.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,27 @@ angular.module('mm.core')
var extractVariableRegex = new RegExp('{{([^|]+)(|.*)?}}', 'i'),
tagsToIgnore = ['AUDIO', 'VIDEO', 'BUTTON', 'INPUT', 'SELECT', 'TEXTAREA', 'A'];

/**
* Add mm-external-content and its extra attributes to a certain element.
*
* @param {Object} el DOM element to add the attributes to.
* @param {String} [component] Component.
* @param {Number} [componentId] Component ID.
* @param {String} [siteId] Site ID.
*/
function addExternalContent(el, component, componentId, siteId) {
el.setAttribute('mm-external-content', '');
if (component) {
el.setAttribute('component', component);
if (componentId) {
el.setAttribute('component-id', componentId);
}
}
if (siteId) {
el.setAttribute('siteid', siteId);
}
}

/**
* Returns the number of characters to shorten the text. If the text shouldn't be shortened, returns undefined.
*
Expand Down Expand Up @@ -80,6 +101,15 @@ angular.module('mm.core')
}
}

/**
* Add class to adapt media to a certain element.
*
* @param {Object} el Dom element to add the class to.
*/
function addMediaAdaptClass(el) {
angular.element(el).addClass('mm-media-adapt-width');
}

/**
* Format contents and render.
*
Expand Down Expand Up @@ -175,44 +205,21 @@ angular.module('mm.core')
return $mmText.formatText(text, attrs.clean, attrs.singleline, shorten).then(function(formatted) {

var el = element[0],
elWidth = el.offsetWidth || el.width || el.clientWidth;

function addMediaAdaptClass(el) {
angular.element(el).addClass('mm-media-adapt-width');
}

// Convert the content into DOM.
var dom = angular.element('<div>').html(formatted);
elWidth = el.offsetWidth || el.width || el.clientWidth,
dom = angular.element('<div>').html(formatted); // Convert the content into DOM.

// Walk through the content to find the links and add our directive to it.
// Important: We need to look for links first because in 'img' we add new links without mm-browser.
angular.forEach(dom.find('a'), function(anchor) {
anchor.setAttribute('mm-external-content', '');
anchor.setAttribute('mm-browser', '');
if (component) {
anchor.setAttribute('component', component);
if (componentId) {
anchor.setAttribute('component-id', componentId);
}
}
if (siteId) {
anchor.setAttribute('siteid', siteId);
}
addExternalContent(anchor, component, componentId, siteId);
});

// Walk through the content to find images, and add our directive.
angular.forEach(dom.find('img'), function(img) {
addMediaAdaptClass(img);
img.setAttribute('mm-external-content', '');
if (component) {
img.setAttribute('component', component);
if (componentId) {
img.setAttribute('component-id', componentId);
}
}
if (siteId) {
img.setAttribute('siteid', siteId);
}
addExternalContent(img, component, componentId, siteId);

// Check if image width has been adapted. If so, add an icon to view the image at full size.
var imgWidth = img.offsetWidth || img.width || img.clientWidth;
if (imgWidth > elWidth) {
Expand All @@ -228,8 +235,12 @@ angular.module('mm.core')
}
});

angular.forEach(dom.find('audio'), addMediaAdaptClass);
angular.forEach(dom.find('video'), addMediaAdaptClass);
angular.forEach(dom.find('audio'), function(el) {
treatMedia(el, component, componentId, siteId);
});
angular.forEach(dom.find('video'), function(el) {
treatMedia(el, component, componentId, siteId);
});
angular.forEach(dom.find('iframe'), addMediaAdaptClass);

return dom.html();
Expand All @@ -255,6 +266,25 @@ angular.module('mm.core')
}
}

/**
* Add media adapt class and mm-external-content to the media element and their child sources.
*
* @param {Object} el DOM element.
* @param {String} [component] Component.
* @param {Number} [componentId] Component ID.
* @param {String} [siteId] Site ID.
*/
function treatMedia(el, component, componentId, siteId) {
addMediaAdaptClass(el);

addExternalContent(el, component, componentId, siteId);
angular.forEach(angular.element(el).find('source'), function(source) {
source.setAttribute('target-src', source.getAttribute('src'));
source.removeAttribute('src');
addExternalContent(source, component, componentId, siteId);
});
}

return {
restrict: 'E',
scope: true,
Expand Down