| var List = require('css-tree').List; | 
| var resolveKeyword = require('css-tree').keyword; | 
| var hasOwnProperty = Object.prototype.hasOwnProperty; | 
| var walk = require('css-tree').walk; | 
|   | 
| function addRuleToMap(map, item, list, single) { | 
|     var node = item.data; | 
|     var name = resolveKeyword(node.name).basename; | 
|     var id = node.name.toLowerCase() + '/' + (node.prelude ? node.prelude.id : null); | 
|   | 
|     if (!hasOwnProperty.call(map, name)) { | 
|         map[name] = Object.create(null); | 
|     } | 
|   | 
|     if (single) { | 
|         delete map[name][id]; | 
|     } | 
|   | 
|     if (!hasOwnProperty.call(map[name], id)) { | 
|         map[name][id] = new List(); | 
|     } | 
|   | 
|     map[name][id].append(list.remove(item)); | 
| } | 
|   | 
| function relocateAtrules(ast, options) { | 
|     var collected = Object.create(null); | 
|     var topInjectPoint = null; | 
|   | 
|     ast.children.each(function(node, item, list) { | 
|         if (node.type === 'Atrule') { | 
|             var name = resolveKeyword(node.name).basename; | 
|   | 
|             switch (name) { | 
|                 case 'keyframes': | 
|                     addRuleToMap(collected, item, list, true); | 
|                     return; | 
|   | 
|                 case 'media': | 
|                     if (options.forceMediaMerge) { | 
|                         addRuleToMap(collected, item, list, false); | 
|                         return; | 
|                     } | 
|                     break; | 
|             } | 
|   | 
|             if (topInjectPoint === null && | 
|                 name !== 'charset' && | 
|                 name !== 'import') { | 
|                 topInjectPoint = item; | 
|             } | 
|         } else { | 
|             if (topInjectPoint === null) { | 
|                 topInjectPoint = item; | 
|             } | 
|         } | 
|     }); | 
|   | 
|     for (var atrule in collected) { | 
|         for (var id in collected[atrule]) { | 
|             ast.children.insertList( | 
|                 collected[atrule][id], | 
|                 atrule === 'media' ? null : topInjectPoint | 
|             ); | 
|         } | 
|     } | 
| }; | 
|   | 
| function isMediaRule(node) { | 
|     return node.type === 'Atrule' && node.name === 'media'; | 
| } | 
|   | 
| function processAtrule(node, item, list) { | 
|     if (!isMediaRule(node)) { | 
|         return; | 
|     } | 
|   | 
|     var prev = item.prev && item.prev.data; | 
|   | 
|     if (!prev || !isMediaRule(prev)) { | 
|         return; | 
|     } | 
|   | 
|     // merge @media with same query | 
|     if (node.prelude && | 
|         prev.prelude && | 
|         node.prelude.id === prev.prelude.id) { | 
|         prev.block.children.appendList(node.block.children); | 
|         list.remove(item); | 
|   | 
|         // TODO: use it when we can refer to several points in source | 
|         // prev.loc = { | 
|         //     primary: prev.loc, | 
|         //     merged: node.loc | 
|         // }; | 
|     } | 
| } | 
|   | 
| module.exports = function rejoinAtrule(ast, options) { | 
|     relocateAtrules(ast, options); | 
|   | 
|     walk(ast, { | 
|         visit: 'Atrule', | 
|         reverse: true, | 
|         enter: processAtrule | 
|     }); | 
| }; |