I've been trying to figure out a way to use $mdTheming service provided by angular-material library to apply theme configured through $mdThemingProvider.
In Simpler terms:
Consider an element
<div class="md-primary" theme></div>
The theme directive in this case has to inject the primary color configure to the theme.
Below is the approach I tried to use, after going through angular-material's code
Just to see how they might have done it
export function ThemeDirective($mdTheming) {
'ngInject';
let directive = {
restrict: 'A',
link: (scope, element) => {
$mdTheming(element);
}
};
return directive
}
The above code doesn't seem to be doing anything. I'm sure I've overlooked something. Need help.
This may help in your research... you can use it towards applying a theme to your directive. I started with the link https://material.angularjs.org/latest/Theming/05_under_the_hood
When you look further into the angular code, you'll find the constant $MD_THEME_CSS. This is basically a css file with all the styles for each of their directives - but it is formatted with tags that they interpolate:
...
md-input-container.md-THEME_NAME-theme .md-input {
color: {{foreground-1}};
border-color: {{foreground-4}};
text-shadow: {{foreground-shadow}};
}
md-input-container.md-THEME_NAME-theme .md-input::-webkit-input-placeholder,md-input-container.md-THEME_NAME-theme .md-input::-moz-placeholder,md-input-container.md-THEME_NAME-theme .md-input:-moz-placeholder,md-input-container.md-THEME_NAME-theme .md-input:-ms-input-placeholder {
color: {{foreground-3}};
}
...
These tags they replace with color values are shown in the documentation of the mdThemingProvider...
/* Some Example Valid Theming Expressions
* =======================================
* Intention group expansion: (valid for primary, accent, warn, background)
* {{primary-100}} - grab shade 100 from the primary palette
* {{primary-100-0.7}} - grab shade 100, apply opacity of 0.7
* {{primary-100-contrast}} - grab shade 100's contrast color
* {{primary-hue-1}} - grab the shade assigned to hue-1 from the primary palette
* {{primary-hue-1-0.7}} - apply 0.7 opacity to primary-hue-1
* {{primary-color}} - Generates .md-hue-1, .md-hue-2, .md-hue-3 with configured shades set for each hue
* {{primary-color-0.7}} - Apply 0.7 opacity to each of the above rules
* {{primary-contrast}} - Generates .md-hue-1, .md-hue-2, .md-hue-3 with configured contrast (ie. text) color shades set for each hue
* {{primary-contrast-0.7}} - Apply 0.7 opacity to each of the above rules
*
* Foreground expansion: Applies rgba to black/white foreground text
* {{foreground-1}} - used for primary text
* {{foreground-2}} - used for secondary text/divider
* {{foreground-3}} - used for disabled text
* {{foreground-4}} - used for dividers
*/
This string is then formatted at run time by generateAllThemes() after all the themes have been defined. This will inject these styles into the <head> element - as you can see by inspecting your page in chrome:
enter image description here
Now I've never done this before personally so i don't know the standards here, and cannot find it documented. But I'm assuming you can call GenerateTheme() somehow to generate your own styles to use in your html... or maybe just borrow some classes that have already been generated for the core code.
I have however done something similar using my own services rather than theirs.
Here's a possible start to your solution that I've used in the past... I wrote a simple provider to save my theme palette after i've defined the theme in my .config(). Then i wrote a service to connect the theme color codes to the actual rgb colors. Hopefully it's not too hacky.
(function() {
var app = angular.module('MyApp', ['ngMaterial', 'ngMessages'])
// The provider... I store the color palette here since a service isn't available during .config();
.provider('colorPalette', function colorPaletteProvider() {
var _PALETTE = {};
this.SetPalette = function(value) {
_PALETTE = value
}
this.$get = [
function() {
return _PALETTE;
}
];
})
.config(function($mdThemingProvider, colorPaletteProvider) {
var xoMap = $mdThemingProvider.extendPalette('purple', {
'500': '833A96'
});
$mdThemingProvider.definePalette('XO-Main', xoMap);
// add a couple of themes
$mdThemingProvider.theme('default')
.primaryPalette('XO-Main')
.accentPalette('pink', {
"default": "500",
"hue-1": "50"
})
.backgroundPalette('grey');
$mdThemingProvider.theme('order')
.primaryPalette('XO-Main')
.accentPalette('light-blue', {
"default": "500",
"hue-1": "50"
});
// save the palette so i can see it later
colorPaletteProvider.SetPalette($mdThemingProvider._PALETTES);
})
.run(function($interpolate, themeColorsService) {
// inject some styles into the head
var orderTheme = themeColorsService.GetColors("order");
var myStyle = $interpolate(".nav-icon-order {color: {{accent.default.bg}};}")(orderTheme);
console.debug(myStyle);
themeColorsService.AddStyle(myStyle);
themeColorsService.AddStyle($interpolate("md-toolbar.hpbx-toolbar-accent-order, .panel-heading.hpbx-toolbar-accent-order { border-bottom: 5px solid {{accent.default.bg}};}")(orderTheme));
});
// The controller
app.controller("AppCtrl", function($scope, themeColorsService) {
$scope.themeColors = themeColorsService.GetColors("default");
});
})();
// example directive where the theme is passed in
angular.module('MyApp').directive('theme', function (themeColorsService) {
return {
restrict: "A",
transclude: true,
template: "<div ng-style='{color: themeColors.primary.default.bg}' ng-transclude></div>",
scope: {
themeName: "="
},
controller: function ($scope, $element, $attrs) {
$scope.themeColors = themeColorsService.GetColors("default");
}
}
});
// The service
angular.module('MyApp').service("themeColorsService", function(colorPalette, $mdTheming) {
this.themes = {};
// tie the color codes together with the palettes
this.GetColors = function(theme) {
var returnVal = {};
if (!this.themes.hasOwnProperty(theme)) {
theme = $mdTheming.THEMES[theme];
_.keys(theme.colors).forEach(function(key) {
returnVal[key] = {};
var palette = theme.colors[key].name;
if (!_.isUndefined(palette)) {
_.keys(theme.colors[key].hues).forEach(function(hue) {
var c = theme.colors[key].hues[hue];
var p = colorPalette[palette][c];
returnVal[key][hue] = {};
returnVal[key][hue].bg = getRGB(p.value);
returnVal[key][hue].fg = getRGB(p.contrast);
});
}
});
this.themes[theme] = _.cloneDeep(returnVal);
}
return this.themes[theme];
};
var getRGB = function(value) {
var returnVal = "";
if (value.length == 4) {
returnVal = "rgba(" + value[0] + "," + value[1] + "," + value[2] + "," + value[3] + ")";
} else if (value.length == 3) {
returnVal = "rgb(" + value[0] + "," + value[1] + "," + value[2] + ")";
}
return returnVal;
};
// insert a style into the head element
this.AddStyle = function(styleContent) {
var head = document.head;
var firstChild = head ? head.firstElementChild : null;
var style = document.createElement('style');
style.setAttribute('xo-theme-style', '');
style.appendChild(document.createTextNode(styleContent));
head.insertBefore(style, firstChild);
};
});
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular-animate.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/3.10.1/lodash.min.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular-animate.min.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular-messages.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular-route.min.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular-aria.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-material/1.0.6/angular-material.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/angular-material/1.0.6/angular-material.css" rel="stylesheet" />
<html ng-app="MyApp">
<body>
<div ng-controller="AppCtrl">
<div>I'm unstyled</div>
<!-- style using ng-style -->
<div ng-style="{color: themeColors.primary.default.bg}">I'm styled with ngStyle</div>
<!-- use your injected css style -->
<div class="nav-icon-order">I'm using an injected css class</div>
<!-- send it to a directive the way you listed in your example -->
<div theme>This is a styled directive</div>
<!-- show the themeColors object -->
<pre>themeColors:{{themeColors | json}}</pre>
</div>
</body>
</html>
Related
There are extensions that make small on-page popups that in Inspection in Chrome show as #shadow-root(open).
I want to customise its styles with Tampermonkey, but using GM_addStyle(".class_name{zoom: 166%;}"); doesn't work. On native elements it works.
GM_addStyle() appends the <style> element it creates to the <head>, therefore it can't modify the styling of Shadow DOM elements. You need to use GM_addElement().
// ==UserScript==
// #name Shadow DOM styling
// #version 0.1
// #author double-beep
// #match http*://example.com
// #grant GM_addElement
// ==/UserScript==
(function() {
'use strict';
const shadowRoot = document.querySelector('element');
const styles = `div { color: red; }`;
GM_addElement(shadowRoot, 'style', { textContent: styles });
})();
If the styles aren't applied, you can create a <script> element instead in a similar way:
GM_addElement(shadowRoot, 'script', {
textContent: 'shadowRootElement.shadowRoot.querySelector("...").style.color = "red";'
});
You need to insert the style tag into the shadow root.
(pinching some code from this answer)
function addStyleToShadowRoot(shadowRoot,css) {
const style = document.createElement('style');
style.type = 'text/css';
style.innerHTML = css;
shadowRoot.appendChild(style);
}
Call it like this:
addStyleToShadowRoot(customElement.shadowRoot, myCss);
I'm trying to make a virtual scroll and whenever the user scrolls down I need to add a negative top equal to the container height to each row. But of course this top property can vary depending of some factors like the user's screen resolution or browser window size.
So far this is what I got:
<div class="container" id="my-container">
<!--If it has the class row-scrolled the top property is applied-->
<div ng-repeat="(row) in virtualCollection"
ng-class="{'row-scrolled': controller.isScrolled}">
<!-- row properties -->
</div>
</div>
I have also thought about the idea of using ng-style but would override any style from my .css file.
Is there anyway to get the size/property of a DOM element...
// controller
var containerHeight = angular.element('#my-container')[0].clientHeight;
var cssProperty = '-' + containerHeight + 'px';
And then use it in an css?
// css
.row-scrolled {
top: cssProperty;
}
You can't pass variables from javascript to CSS since CSS is not a programming language but a style sheet language.
What you can do is manipulating specific elements with javascript.
Based on your code here is an example:
// controller
var containerHeight = angular.element('#my-container')[0].clientHeight;
var cssProperty = '-' + containerHeight + 'px';
var $$rowScrolled = document.querySelectorAll(".row-scrolled");
if ($$rowScrolled && $$rowScrolled.length > 0) {
for (var i = 0; i < $$rowScrolled.length; i++) {
var $rowScrolled = $$rowScrolled[i];
$rowScrolled.style.top = cssProperty;
}
}
With jQuery:
// controller
var containerHeight = angular.element('#my-container')[0].clientHeight;
var cssProperty = '-' + containerHeight + 'px';
var $rowScrolled = $(".row-scrolled");
if ($rowScrolled && $rowScrolled.length > 0) {
$rowScrolled.css("top", cssProperty);
}
You can not pass a variable to the CSS.
What you can do though is add the property directly using the ng-style tag:
<div class="container" id="my-container">
<!--If it has the class row-scrolled the top property is applied-->
<div ng-repeat="(row) in virtualCollection"
ng-style="{'top': controller.cssProperty}">
<!-- row properties -->
</div>
I have a select with ngOptions based on an array. This array can change.
If the new array value does not contain the selected option value, the option value is set to undefined by the selectController. Is there a way to prevent this ?
Plunker : https://plnkr.co/edit/kao3h5ivHXlP1Wrdx1Ib?p=preview
Scenario:
select Blue/Red or Green color
click on Reduced to only have Black and White options
See that the model value is left to blank
Wanted behavior : that the model value stays at Blue/Red or Green
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<script src="//code.angularjs.org/snapshot/angular.min.js"></script>
</head>
<body ng-app="selectExample">
<script>
angular.module('selectExample', [])
.controller('ExampleController', ['$scope', function($scope) {
$scope.colorsFull = [
{id:"bk", name:'black'},
{id:"w", name:'white'},
{id:"r", name:'red'},
{id:"be", name:'blue'},
{id:"y", name:'yellow'}
];
$scope.colors = $scope.colorsFull;
$scope.selectedColor =$scope.colorsFull[0];
$scope.colorsReduced = [
{id:"bk", name:'black2'},
{id:"w", name:'white2'}];
}]);
</script>
<div ng-controller="ExampleController">
<button ng-click="colors=colorsReduced">Reduced</button>
<button ng-click="colors=colorsFull">Full</button>
<br/>
Colors : {{colors}}
<hr/>
<select ng-model="selectedColor" ng-options="color.name for color in colors track by color.id">
</select>
selectedColor:{{selectedColor}}
</div>
</body>
</html>
You can achieve this by keeping track of what color is selected in the full colors dropdown, and inserting it into the reduced colors array. First, add an ng-change directive so that we can keep track of the selected color:
<select ng-model="selectedColor" ng-options="color.name for color in colors track by color.id" ng-change="setColor(selectedColor)">
And in your controller:
$scope.setColor = function(color) {
if(color !== null) {
// Keep track of the color that is selected
$scope.previousColor = color;
}
else {
// Changed arrays, keep selected color in model
$scope.selectedColor = $scope.previousColor;
}
}
Now ng-model is set to the correct color whenever the arrays are changed, but it will appear blank in the reduced colors dropdown because the option doesn't exist. So, we need to insert that option into the array. However, switching back and forth between dropdowns will cause the reduced colors array to keep on adding more options, and we only want to remember the option we selected from the full colors array. So, we need create an initial set of colors to revert back to when switching.
// Keep a copy of the original set of reduced colors
$scope.colorsReducedInitial = [
{id:"bk", name:'black2'},
{id:"w", name:'white2'}];
Finally, we need to insert the selected option into the reduced colors array. Change the ng-click on the Reduced button to use a function:
<button ng-click="setColorsReduced()">Reduced</button>
Now, we can insert the option, after resetting the reduced colors array to its initial state:
$scope.setColorsReduced = function() {
// Revert back to the initial set of reduced colors
$scope.colors = angular.copy($scope.colorsReducedInitial);
if($scope.previousColor !== undefined) {
var found = false;
angular.forEach($scope.colorsReducedInitial, function(value, key) {
if(value.id == $scope.previousColor.id) {
found = true;
}
});
// If the id is found, no need to push the previousColor
if(!found) {
$scope.colors.push($scope.previousColor);
}
}
}
Note that we are looping through the reduced colors array to ensure we aren't duplicating any colors, such as black or white.
Now, the reduced colors ng-model has the previous dropdown's selected color.
Updated Plunkr Demo
Use below code in script
$scope.makeSelected=function(){
$scope.selectedColor =$scope.colorsReduced[0];
}
And just add this function call in reduced button line like below
<button ng-click="colors=colorsReduced;makeSelected()">Reduced</button>
This will do what you want to achieve.
Using Jukebox's answer, I ended-up writing a directive, using the modelCtrl.$formatters to get the initial value. It also offer the possibility to store the previousValue in the scope or in a local variable :
Usage: <select .... select-keep> or <select .... select-keep="previousColor">
Directive:
.directive('selectKeep', function($parse) {
return {
require: 'ngModel',
link: function (scope, element, attrs, modelCtrl) {
var previousValueGetter;
var previousValueSetter;
if (attrs.selectKeep) { //use a scope attribute to store the previousValue
previousValueGetter = $parse(attrs.selectKeep);
previousValueSetter = previousValueGetter.assign;
}
else { //use a local variable to store the previousValue
var previousValue;
previousValueGetter = function(s) { return previousValue;};
previousValueSetter = function(s, v) { previousValue = v;};
}
//store the initial value
modelCtrl.$formatters.push(function(v) {
previousValueSetter(scope, v);
return v;
});
//get notified of model changes (copied from Jukebox's answer)
modelCtrl.$viewChangeListeners.push(function() {
if (modelCtrl.$modelValue !== null) {
previousValueSetter(scope, modelCtrl.$modelValue);
} else {
modelCtrl.$setViewValue(previousValueGetter(scope));
}
});
}
};
Plunker
Edit : it has a flaw, the form gets dirty even if the value does not change. I had to add these lines in the else of the viewChangeListener but it doesn't look nice. Any ideas ?:
...
} else {
modelCtrl.$setViewValue(previousValueGetter(scope));
//set pristine since this change is not a real change
modelCtrl.$setPristine(true);
//check if any other modelCtrl is dirty. If not, we will have to put the form as pristine too
var oneDirty =_.findKey(modelCtrl.$$parentForm, function(otherModelCtrl) {
return otherModelCtrl && otherModelCtrl.hasOwnProperty('$modelValue') && otherModelCtrl !== modelCtrl && otherModelCtrl.$dirty;
});
if (!oneDirty) {
modelCtrl.$$parentForm.$setPristine(true);
}
}
I have an app with <ons-sliding-menu> and a page with <ons-toolbar> and a horizontal <ons-carousel> covering the remaining space.
For the <ons-sliding-menu> the parameter swipe-target-width="50px" is set.
Is there a way to tell the <ons-carousel> to ignore events originating from the most left 50px and let these go to the menu?
Currently there is no option to make the carousel ignore events on one side, but perhaps you can make a trick. You can put a div at the same level than the carousel and let it take the clicks instead of the carousel in the area you need:
<div class="cover"></div>
<ons-carousel>
...
</ons-carousel>
You can change these values to fit your case:
.cover {
position: absolute;
left: 0;
height: 100%;
width: 200px;
z-index: 1;
}
Check it out here: http://codepen.io/frankdiox/pen/YqKOJE
Hope it helps!
After some experimentation, I came to the solution to inject the necessary functionality directly in the drag event handlers of the OnsCarouselElement. For this purpose I have introduced the attribute swipe-ignore-left for the <ons-carousel>. The other sites could easily be added when needed.In order to inject the functionality, load this JS-Code after loading onsenui.js:
(function () {
'use strict';
/****************************************************************
Checks the current event against the attribute swipe-ignore-left.
****************************************************************/
window.OnsCarouselElement.prototype._ignoreDrag = function (event) {
var attr = this.getAttribute('swipe-ignore-left');
if (attr === undefined) return false;
var left = parseInt(attr, 10);
if (left === undefined || left < 1) return false;
var startX = event.gesture.center.clientX - event.gesture.deltaX;
return startX < left;
};
/****************************************************************
Save the original drag-event-handlers
****************************************************************/
var originalCarouselOnDrag = window.OnsCarouselElement.prototype._onDrag;
var originalCarouselOnDragEnd = window.OnsCarouselElement.prototype._onDragEnd;
/****************************************************************
Override: OnsCarouselElement.prototype._onDrag
****************************************************************/
window.OnsCarouselElement.prototype._onDrag = function (event) {
if (this._ignoreDrag(event)) return;
originalCarouselOnDrag.apply(this, arguments);
};
/****************************************************************
Override: OnsCarouselElement.prototype._onDragEnd
****************************************************************/
window.OnsCarouselElement.prototype._onDragEnd = function (event) {
if (this._ignoreDrag(event)) return;
originalCarouselOnDragEnd.apply(this, arguments);
};
})();
To preserve for example the left 20 pixel for the <ons-sliding-menu>, this HTML is to provide:
<ons-sliding-menu ... side="left" swipeable swipe-target-width="20px" />
...
<ons-carousel ... swipeable swipe-ignore-left="20px" />
Had created and used my custom polymer element which is a table. Now, I want to use the check box element from their catalog in my table.
However, I keep getting this error when I reference the check box html file in my index page:
DuplicateDefinitionError: a type with name 'dom-module' is already
registered
This is how I have created my custom element:
<!-- Imports polymer -->
<link rel="import" href="polymer/polymer.html">
<script src="underscore-min.js"></script>
<!-- Defines element markup -->
<dom-module id="custom-table" >
<template>
<style>
ul {list-style-type:none; display:block}
ul li {display:inline; float:left; padding:20px; width:1.5em; border-bottom:1px solid #eee}
</style>
<h2>{{title}}</h2>
<table id="dataTable">
<thead id="tableHead"></thead>
<tbody id="tableBody"></tbody>
</table>
</template>
</dom-module>
<!-- Registers custom element -->
<script>
Polymer({
is: 'custom-table',
// Fires when an instance of the element is created
created: function() {
},
// Fires when the local DOM has been fully prepared
ready: function() {
var context= this;
this.pageNo=0;
this.totalPages=0;
// set the default paging size:
if(this.page== null|| this.page==undefined)
this.page=10;
// delegate the change selection handler to the table body
this.$.tableBody.addEventListener("click",function(e){
if(e.target && e.target.nodeName == "INPUT") ;
{
context.changeSelection(e.target);
}
});
},
// Fires when the element was inserted into the document
attached: function() {},
// Fires when the element was removed from the document
detached: function() {},
// Fires when an attribute was added, removed, or updated
attributeChanged: function(name, type) {
alert("changed");
},
loadData: function(columns,data){
this.data = data;
// add the selected property to the values
for(var i=0;i<this.data.length; i++) { this.data[i].Selected = false;}
this.filteredData=this.data;
this.columns = columns;
//initialize the filteredData
this.filteredData=data;
// calculate the total number of pages
this.totalPages= Math.ceil(data.length/this.page);
this.drawTableHeader();
_.defer(this.applyFilters,this);
_.defer(this.drawTableBody,this);
},
drawTableHeader:function(){
var columns = this.columns;
// load the header
var headTr = document.createElement('tr');
//add a blank header for the check box;
var th=document.createElement('th');
headTr.appendChild(th);
for(var i = 0; i<columns.length ;i++)
{
var td=document.createElement('th');
// if the column is sortable then add the event listener for sorting it
if(columns[i].Sortable)
{
td.addEventListener("click",function(){ this.sortBy(columns[i].Title); });
}
td.innerText = columns[i].Title;
headTr.appendChild(td);
}
this.$.tableHead.appendChild(headTr);
},
drawTableBody: function(context){
// this is a defered function
var context = context;
// get the number of items according to the current page number
var pageItems= context.filteredData.slice((context.page*context.pageNo),((context.page*context.pageNo)+context.page));
console.log(pageItems);
// print the page items
for(var i=0; i < pageItems.length; i++)
{
var currItem = pageItems[i];
var tr= document.createElement("tr");
// add the check box first
var checkbox= document.createElement("input");
checkbox.type="checkbox";
checkbox.checked=pageItems[i].Selected;
var ItemId = currItem.Id;
checkbox.setAttribute("data-ItemId",ItemId-1);
var td=document.createElement('td');
td.appendChild(checkbox);
tr.appendChild(td);
// for every column specified add a column to it
for(var j = 0; j< context.columns.length; j++)
{
var td=document.createElement("td");
td.innerText= pageItems[i][context.columns[j].Title];
tr.appendChild(td);
}
//append the row to the table;
context.$.tableBody.appendChild(tr);
} // end for i
},
applyFilters:function(context){
if(context.filter)
{
alert("filterApplied");
}
},
changeSelection:function(checkbox){
var ItemId = checkbox.getAttribute("data-ItemId");
this.data[ItemId].Selected= checkbox.checked;
console.log(this.data[ItemId]);
},
properties:{
title :String,
columns:Array,
data:Array,
page:Number,
filters:Object,
Selectable:Boolean
}
});
</script>
and here is what my index page looks like:
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title><my-repo></title>
<!-- Imports polyfill -->
<script src="webcomponents-lite.min.js"></script>
<!-- Imports custom element -->
<link rel="import" href="my-element.html">
<link rel="import" href="bower_components/paper-checkbox/paper-checkbox.html">
</head>
<body unresolved>
<!-- Runs custom element -->
<custom-table title="This is data table"></custom-table>
<script>
document.addEventListener("WebComponentsReady",function(){
var data = [{'Id':1,'firstName':'aman',age:25},{'Id':2,'firstName':'gupta',age:25}];
var cols = [{Title:'firstName',Sortable:true},{Title:'age',Sortable:false}];
var a = document.querySelector('my-element');
a.loadData(cols,data);
});
</script>
</body>
</html>
I've just started out with polymer and I'm not quite sure what's going on here..
Thank you in advance :)
I got what the problem is..
My custom element was referencing a different Polymer.html file.
Silly me :D
I'm using Polymer Starter Kit Yeoman generator on Windows and I had the same problem:
Error: DuplicateDefinitionError: a type with name 'dom-module' is already registered
This error is triggered in Firefox console. Chrome works fine.
The components created with the generator (example: yo polymer:el my-element) have this polymer.html import:
<link rel="import" href="..\..\bower_components/polymer/polymer.html">
The base path is described with "backslash".
In some custom polymer elements I created by myself, I imported polymer.html with:
<link rel="import" href="../../bower_components/polymer/polymer.html">
And I think this lead to a duplication of some kind. To solve the problem, I just changed all automatically created imports, using only forward slashes /.
Hope this helps someone.